diff options
-rw-r--r-- | base/platform_file.h | 7 | ||||
-rw-r--r-- | base/platform_file_posix.cc | 10 | ||||
-rw-r--r-- | base/platform_file_win.cc | 4 | ||||
-rw-r--r-- | content/content_browser.gypi | 2 | ||||
-rw-r--r-- | webkit/fileapi/file_system_directory_database.cc | 57 | ||||
-rw-r--r-- | webkit/fileapi/file_system_directory_database.h | 11 | ||||
-rw-r--r-- | webkit/fileapi/file_system_directory_database_unittest.cc | 4 | ||||
-rw-r--r-- | webkit/fileapi/file_system_file_util.cc | 3 | ||||
-rw-r--r-- | webkit/fileapi/file_system_file_util.h | 3 | ||||
-rw-r--r-- | webkit/fileapi/obfuscated_file_system_file_util.cc | 766 | ||||
-rw-r--r-- | webkit/fileapi/obfuscated_file_system_file_util.h | 161 | ||||
-rw-r--r-- | webkit/fileapi/obfuscated_file_system_file_util_unittest.cc | 763 | ||||
-rw-r--r-- | webkit/fileapi/sandbox_mount_point_provider.cc | 5 | ||||
-rw-r--r-- | webkit/fileapi/sandbox_mount_point_provider.h | 8 | ||||
-rw-r--r-- | webkit/fileapi/webkit_fileapi.gypi | 2 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 2 |
16 files changed, 1792 insertions, 16 deletions
diff --git a/base/platform_file.h b/base/platform_file.h index 461b060..dbac61a 100644 --- a/base/platform_file.h +++ b/base/platform_file.h @@ -96,9 +96,10 @@ struct BASE_API PlatformFileInfo { base::Time creation_time; }; -// Creates or opens the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and -// |created| is provided, |created| will be set to true if the file was created -// or to false in case the file was just opened. |error_code| can be NULL. +// Creates or opens the given file. If |created| is provided, it will be set to +// true if a new file was created [or an old one truncated to zero length to +// simulate a new file, which can happen with PLATFORM_FILE_CREATE_ALWAYS], and +// false otherwise. |error_code| can be NULL. BASE_API PlatformFile CreatePlatformFile(const FilePath& name, int flags, bool* created, diff --git a/base/platform_file_posix.cc b/base/platform_file_posix.cc index 6e2cff6..2ffac06 100644 --- a/base/platform_file_posix.cc +++ b/base/platform_file_posix.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -36,6 +36,9 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags, if (flags & PLATFORM_FILE_CREATE) open_flags = O_CREAT | O_EXCL; + if (created) + *created = false; + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { DCHECK(!open_flags); open_flags = O_CREAT | O_TRUNC; @@ -70,10 +73,7 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags, HANDLE_EINTR(open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR)); if (flags & PLATFORM_FILE_OPEN_ALWAYS) { - if (descriptor > 0) { - if (created) - *created = false; - } else { + if (descriptor <= 0) { open_flags |= O_CREAT; if (flags & PLATFORM_FILE_EXCLUSIVE_READ || flags & PLATFORM_FILE_EXCLUSIVE_WRITE) { diff --git a/base/platform_file_win.cc b/base/platform_file_win.cc index 0cb3e53..7a6a586 100644 --- a/base/platform_file_win.cc +++ b/base/platform_file_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -17,6 +17,8 @@ PlatformFile CreatePlatformFile(const FilePath& name, base::ThreadRestrictions::AssertIOAllowed(); DWORD disposition = 0; + if (created) + *created = false; if (flags & PLATFORM_FILE_OPEN) disposition = OPEN_EXISTING; diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 79c0c63..c930b44 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -13,6 +13,8 @@ '../ppapi/ppapi_internal.gyp:ppapi_proxy', '../skia/skia.gyp:skia', '../third_party/flac/flac.gyp:libflac', + # TODO(ericu): remove leveldb ref after crbug.com/6955013 is fixed. + '../third_party/leveldb/leveldb.gyp:leveldb', '../third_party/speex/speex.gyp:libspeex', '../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:webkit', '../third_party/zlib/zlib.gyp:zlib', diff --git a/webkit/fileapi/file_system_directory_database.cc b/webkit/fileapi/file_system_directory_database.cc index 8290ff1..d3d5bae 100644 --- a/webkit/fileapi/file_system_directory_database.cc +++ b/webkit/fileapi/file_system_directory_database.cc @@ -132,9 +132,31 @@ bool FileSystemDirectoryDatabase::GetChildWithName( return false; } +bool FileSystemDirectoryDatabase::GetFileWithPath( + const FilePath& path, FileId* file_id) { + std::vector<FilePath::StringType> components; + path.GetComponents(&components); + FileId local_id = 0; + std::vector<FilePath::StringType>::iterator iter; + for (iter = components.begin(); iter != components.end(); ++iter) { + std::string name; +#if defined(OS_POSIX) + name = *iter; +#elif defined(OS_WIN) + name = base::SysWideToUTF8(*iter); +#endif + if (name == "/") + continue; + if (!GetChildWithName(local_id, name, &local_id)) + return false; + } + *file_id = local_id; + return true; +} + bool FileSystemDirectoryDatabase::ListChildren( FileId parent_id, std::vector<FileId>* children) { - // Check to add later: fail if parent is a file, in debug builds. + // Check to add later: fail if parent is a file, at least in debug builds. if (!Init()) return false; DCHECK(children); @@ -287,6 +309,36 @@ bool FileSystemDirectoryDatabase::UpdateModificationTime( return true; } +bool FileSystemDirectoryDatabase::OverwritingMoveFile( + FileId src_file_id, FileId dest_file_id) { + FileInfo src_file_info; + FileInfo dest_file_info; + + if (!GetFileInfo(src_file_id, &src_file_info)) + return false; + if (!GetFileInfo(dest_file_id, &dest_file_info)) + return false; + leveldb::WriteBatch batch; + // This is the only field that really gets moved over; if you add fields to + // FileInfo, e.g. ctime, they might need to be copied here. + dest_file_info.data_path = src_file_info.data_path; + if (!RemoveFileInfoHelper(src_file_id, &batch)) + return false; + Pickle pickle; + if (!PickleFromFileInfo(dest_file_info, &pickle)) + return false; + batch.Put( + GetFileLookupKey(dest_file_id), + leveldb::Slice(reinterpret_cast<const char *>(pickle.data()), + pickle.size())); + leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); + if (!status.ok()) { + HandleError(status); + return false; + } + return true; +} + bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { if (!Init()) return false; @@ -394,7 +446,7 @@ bool FileSystemDirectoryDatabase::VerifyIsDirectory(FileId file_id) { return true; // The root is a directory. if (!GetFileInfo(file_id, &info)) return false; - if (!info.data_path.empty()) { + if (!info.is_directory()) { LOG(ERROR) << "New parent directory is a file!"; return false; } @@ -432,6 +484,7 @@ bool FileSystemDirectoryDatabase::RemoveFileInfoHelper( return false; if (info.data_path.empty()) { // It's a directory std::vector<FileId> children; + // TODO(ericu): Make a faster is-the-directory-empty check. if (!ListChildren(file_id, &children)) return false; if(children.size()) { diff --git a/webkit/fileapi/file_system_directory_database.h b/webkit/fileapi/file_system_directory_database.h index 5d281c8..1ac656b 100644 --- a/webkit/fileapi/file_system_directory_database.h +++ b/webkit/fileapi/file_system_directory_database.h @@ -44,8 +44,11 @@ class FileSystemDirectoryDatabase { FileId parent_id; FilePath data_path; + // TODO: Switch name to be FilePath::StringType to cut down on conversions. std::string name; - // We could get this from the file if we touched it on every move. + // This modification time is valid only for directories, not files, as + // FileWriter will get the files out of sync. + // For files, look at the modification time of the underlying data_path. base::Time modification_time; }; @@ -54,6 +57,7 @@ class FileSystemDirectoryDatabase { bool GetChildWithName( FileId parent_id, const std::string& name, FileId* child_id); + bool GetFileWithPath(const FilePath& path, FileId* file_id); // ListChildren will succeed, returning 0 children, if parent_id doesn't // exist. bool ListChildren(FileId parent_id, std::vector<FileId>* children); @@ -66,6 +70,11 @@ class FileSystemDirectoryDatabase { bool UpdateFileInfo(FileId file_id, const FileInfo& info); bool UpdateModificationTime( FileId file_id, const base::Time& modification_time); + // This is used for an overwriting move of a file [not a directory] on top of + // another file [also not a directory]; we need to alter two files' info in a + // single transaction to avoid weird backing file references in the event of a + // partial failure. + bool OverwritingMoveFile(FileId src_file_id, FileId dest_file_id); // This produces the series 0, 1, 2..., starting at 0 when the underlying // filesystem is first created, and maintaining state across diff --git a/webkit/fileapi/file_system_directory_database_unittest.cc b/webkit/fileapi/file_system_directory_database_unittest.cc index 44ed489..af0cae4 100644 --- a/webkit/fileapi/file_system_directory_database_unittest.cc +++ b/webkit/fileapi/file_system_directory_database_unittest.cc @@ -177,6 +177,8 @@ TEST_F(FileSystemDirectoryDatabaseTest, TestGetChildWithName) { EXPECT_EQ(file_id1, check_file_id); } +// TODO(ericu): Test GetFileWithPath. + TEST_F(FileSystemDirectoryDatabaseTest, TestListChildren) { // No children in the root. std::vector<FileId> children; @@ -276,6 +278,8 @@ TEST_F(FileSystemDirectoryDatabaseTest, TestSimpleFileOperations) { EXPECT_EQ(info0.modification_time, info1.modification_time); } +// TODO: Test for OverwritingMoveFile. + TEST_F(FileSystemDirectoryDatabaseTest, TestGetNextInteger) { int64 next; EXPECT_TRUE(db()->GetNextInteger(&next)); diff --git a/webkit/fileapi/file_system_file_util.cc b/webkit/fileapi/file_system_file_util.cc index cbcf548..9349ad03 100644 --- a/webkit/fileapi/file_system_file_util.cc +++ b/webkit/fileapi/file_system_file_util.cc @@ -56,7 +56,8 @@ PlatformFileError FileSystemFileUtil::EnsureFileExists( created, &error_code); if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) { // Make sure created_ is false. - *created = false; + if (created) + *created = false; error_code = base::PLATFORM_FILE_OK; } if (handle != base::kInvalidPlatformFileValue) diff --git a/webkit/fileapi/file_system_file_util.h b/webkit/fileapi/file_system_file_util.h index 9f9eb06..5df8cd4 100644 --- a/webkit/fileapi/file_system_file_util.h +++ b/webkit/fileapi/file_system_file_util.h @@ -256,7 +256,8 @@ class FileSystemFileUtil { // Returns a pointer to a new instance of AbstractFileEnumerator which is // implemented for each FileUtil subclass. The instance needs to be freed - // by the caller. + // by the caller, and its lifetime should not extend past when the current + // call returns to the main FILE message loop. virtual AbstractFileEnumerator* CreateFileEnumerator( FileSystemOperationContext* unused, const FilePath& root_path); diff --git a/webkit/fileapi/obfuscated_file_system_file_util.cc b/webkit/fileapi/obfuscated_file_system_file_util.cc new file mode 100644 index 0000000..2421547 --- /dev/null +++ b/webkit/fileapi/obfuscated_file_system_file_util.cc @@ -0,0 +1,766 @@ +// Copyright (c) 2011 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/fileapi/obfuscated_file_system_file_util.h" + +#include <queue> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/sys_string_conversions.h" +#include "base/stl_util-inl.h" +#include "googleurl/src/gurl.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/fileapi/file_system_path_manager.h" +#include "webkit/fileapi/quota_file_util.h" +#include "webkit/fileapi/sandbox_mount_point_provider.h" + +// TODO(ericu): Every instance of FileSystemFileUtil in this file should switch +// to QuotaFileUtil as soon as I sort out FileSystemPathManager's and +// SandboxMountPointProvider's lookups of the root path for a filesystem. +namespace { + +const char kOriginDatabaseName[] = "Origins"; +const char kDirectoryDatabaseName[] = "Paths"; + +void InitFileInfo( + fileapi::FileSystemDirectoryDatabase::FileInfo* file_info, + fileapi::FileSystemDirectoryDatabase::FileId parent_id, + const FilePath::StringType& file_name, + const FilePath& data_path) { + DCHECK(file_info); + file_info->parent_id = parent_id; + file_info->data_path = data_path; +#if defined(OS_POSIX) + file_info->name = file_name; +#elif defined(OS_WIN) + file_info->name = base::SysWideToUTF8(file_name); +#endif +} + +} // namespace + +namespace fileapi { + +using base::PlatformFile; +using base::PlatformFileError; + +ObfuscatedFileSystemFileUtil::ObfuscatedFileSystemFileUtil( + const FilePath& file_system_directory) + : file_system_directory_(file_system_directory) { +} + +ObfuscatedFileSystemFileUtil::~ObfuscatedFileSystemFileUtil() { + DropDatabases(); +} + +PlatformFileError ObfuscatedFileSystemFileUtil::CreateOrOpen( + FileSystemOperationContext* context, + const FilePath& virtual_path, int file_flags, + PlatformFile* file_handle, bool* created) { + DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE | + base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_EXCLUSIVE_WRITE))); + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) { + // The file doesn't exist. + if (!(file_flags & (base::PLATFORM_FILE_CREATE | + base::PLATFORM_FILE_CREATE_ALWAYS))) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileId parent_id; + if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileInfo file_info; + InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value(), + FilePath()); + PlatformFileError error = CreateFile( + context, context->src_origin_url(), context->src_type(), &file_info, + file_flags, file_handle); + if (created && base::PLATFORM_FILE_OK == error) + *created = true; + return error; + } + if (file_flags & base::PLATFORM_FILE_CREATE) + return base::PLATFORM_FILE_ERROR_EXISTS; + + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (file_info.is_directory()) + return base::PLATFORM_FILE_ERROR_NOT_A_FILE; + return FileSystemFileUtil::GetInstance()->CreateOrOpen( + context, file_info.data_path, file_flags, file_handle, created); +} + +PlatformFileError ObfuscatedFileSystemFileUtil::EnsureFileExists( + FileSystemOperationContext* context, + const FilePath& virtual_path, + bool* created) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (db->GetFileWithPath(virtual_path, &file_id)) { + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (file_info.is_directory()) + return base::PLATFORM_FILE_ERROR_NOT_A_FILE; + if (created) + *created = false; + return base::PLATFORM_FILE_OK; + } + FileId parent_id; + if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + + FileInfo file_info; + InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value(), + FilePath()); + PlatformFileError error = CreateFile(context, + context->src_origin_url(), context->src_type(), &file_info, 0, NULL); + if (created && base::PLATFORM_FILE_OK == error) + *created = true; + return error; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::GetLocalFilePath( + FileSystemOperationContext* context, + const FilePath& virtual_path, + FilePath* local_path) { + FilePath path = + GetLocalPath(context->src_origin_url(), context->src_type(), + virtual_path); + if (path.empty()) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + + *local_path = path; + return base::PLATFORM_FILE_OK; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfo( + FileSystemOperationContext* context, + const FilePath& virtual_path, + base::PlatformFileInfo* file_info, + FilePath* platform_file_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileInfo local_info; + if (!db->GetFileInfo(file_id, &local_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (local_info.is_directory()) { + file_info->is_directory = true; + file_info->is_symbolic_link = false; + file_info->last_modified = local_info.modification_time; + *platform_file_path = FilePath(); + // We don't fill in ctime or atime. + return base::PLATFORM_FILE_OK; + } + if (local_info.data_path.empty()) + return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; + return FileSystemFileUtil::GetInstance()->GetFileInfo( + context, local_info.data_path, file_info, platform_file_path); +} + +PlatformFileError ObfuscatedFileSystemFileUtil::ReadDirectory( + FileSystemOperationContext* context, + const FilePath& virtual_path, + std::vector<base::FileUtilProxy::Entry>* entries) { + // TODO(kkanetkar): Implement directory read in multiple chunks. + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + DCHECK(!file_id); + // It's the root directory and the database hasn't been initialized yet. + entries->clear(); + return base::PLATFORM_FILE_OK; + } + if (!file_info.is_directory()) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + std::vector<FileId> children; + if (!db->ListChildren(file_id, &children)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + std::vector<FileId>::iterator iter; + for (iter = children.begin(); iter != children.end(); ++iter) { + if (!db->GetFileInfo(*iter, &file_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + base::FileUtilProxy::Entry entry; +#if defined(OS_POSIX) + entry.name = file_info.name; +#elif defined(OS_WIN) + entry.name = base::SysUTF8ToWide(file_info.name); +#endif + entry.is_directory = file_info.is_directory(); + entries->push_back(entry); + } + return base::PLATFORM_FILE_OK; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::CreateDirectory( + FileSystemOperationContext* context, + const FilePath& virtual_path, + bool exclusive, + bool recursive) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (db->GetFileWithPath(virtual_path, &file_id)) { + FileInfo file_info; + if (exclusive) + return base::PLATFORM_FILE_ERROR_EXISTS; + if (!db->GetFileInfo(file_id, &file_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (!file_info.is_directory()) + return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; + return base::PLATFORM_FILE_OK; + } + + std::vector<FilePath::StringType> components; + virtual_path.GetComponents(&components); + FileId parent_id = 0; + size_t index; + for (index = 0; index < components.size(); ++index) { + std::string name; +#if defined(OS_POSIX) + name = components[index]; +#elif defined(OS_WIN) + name = base::SysWideToUTF8(components[index]); +#endif + if (name == "/") + continue; + if (!db->GetChildWithName(parent_id, name, &parent_id)) + break; + } + if (!recursive && components.size() - index > 1) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + for (; index < components.size(); ++index) { + FileInfo file_info; +#if defined(OS_POSIX) + file_info.name = components[index]; +#elif defined(OS_WIN) + file_info.name = base::SysWideToUTF8(components[index]); +#endif + if (file_info.name == "/") + continue; + file_info.modification_time = base::Time::Now(); + file_info.parent_id = parent_id; + if (!db->AddFileInfo(file_info, &parent_id)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + } + return base::PLATFORM_FILE_OK; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::CopyOrMoveFile( + FileSystemOperationContext* context, + const FilePath& src_file_path, + const FilePath& dest_file_path, + bool copy) { + // TODO(ericu): Handle multi-db move+copy, where src and dest aren't in the + // same database. Currently we'll just fail badly. This may get handled from + // higher-level code, though, and as we don't have cross-filesystem + // transactions that's not any less efficient than doing it here. + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId src_file_id; + if (!db->GetFileWithPath(src_file_path, &src_file_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileId dest_file_id; + bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id); + FileInfo src_file_info; + FileInfo dest_file_info; + if (!db->GetFileInfo(src_file_id, &src_file_info) || + src_file_info.is_directory()) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (overwrite) { + if (!db->GetFileInfo(dest_file_id, &dest_file_info) || + dest_file_info.is_directory()) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + } + /* + * Copy-with-overwrite + * Just overwrite data file + * Copy-without-overwrite + * Copy backing file + * Create new metadata pointing to new backing file. + * Move-with-overwrite + * transaction: + * Remove source entry. + * Point target entry to source entry's backing file. + * Delete target entry's old backing file + * Move-without-overwrite + * Just update metadata + */ + if (copy) { + if (overwrite) { + return FileSystemFileUtil::GetInstance()->CopyOrMoveFile(context, + src_file_info.data_path, dest_file_info.data_path, copy); + } else { + FileId dest_parent_id; + if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { + NOTREACHED(); // We shouldn't be called in this case. + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + } + InitFileInfo(&dest_file_info, dest_parent_id, + dest_file_path.BaseName().value(), src_file_info.data_path); + return CreateFile(context, context->dest_origin_url(), + context->dest_type(), &dest_file_info, 0, NULL); + } + } else { // It's a move. + if (overwrite) { + if (!db->OverwritingMoveFile(src_file_id, dest_file_id)) + return base::PLATFORM_FILE_ERROR_FAILED; + if (base::PLATFORM_FILE_OK != + FileSystemFileUtil::GetInstance()->DeleteFile( + context, dest_file_info.data_path)) + LOG(WARNING) << "Leaked a backing file."; + return base::PLATFORM_FILE_OK; + } else { + FileId dest_parent_id; + if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + src_file_info.parent_id = dest_parent_id; +#if defined(OS_POSIX) + src_file_info.name = dest_file_path.BaseName().value(); +#elif defined(OS_WIN) + src_file_info.name = base::SysWideToUTF8( + dest_file_path.BaseName().value()); +#endif + if (!db->UpdateFileInfo(src_file_id, src_file_info)) + return base::PLATFORM_FILE_ERROR_FAILED; + return base::PLATFORM_FILE_OK; + } + } + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::DeleteFile( + FileSystemOperationContext* context, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (!db->RemoveFileInfo(file_id)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (base::PLATFORM_FILE_OK != + FileSystemFileUtil::GetInstance()->DeleteFile( + context, file_info.data_path)) + LOG(WARNING) << "Leaked a backing file."; + return base::PLATFORM_FILE_OK; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::DeleteSingleDirectory( + FileSystemOperationContext* context, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info) || !file_info.is_directory()) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (!db->RemoveFileInfo(file_id)) + return base::PLATFORM_FILE_ERROR_FAILED; + return base::PLATFORM_FILE_OK; +} + +PlatformFileError ObfuscatedFileSystemFileUtil::Touch( + FileSystemOperationContext* context, + const FilePath& virtual_path, + const base::Time& last_access_time, + const base::Time& last_modified_time) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return base::PLATFORM_FILE_ERROR_FAILED; + FileId file_id; + if (db->GetFileWithPath(virtual_path, &file_id)) { + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + NOTREACHED(); + return base::PLATFORM_FILE_ERROR_FAILED; + } + if (file_info.is_directory()) { + file_info.modification_time = last_modified_time; + if (!db->UpdateFileInfo(file_id, file_info)) + return base::PLATFORM_FILE_ERROR_FAILED; + return base::PLATFORM_FILE_OK; + } + return FileSystemFileUtil::GetInstance()->Touch( + context, file_info.data_path, last_access_time, last_modified_time); + } + FileId parent_id; + if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + + FileInfo file_info; + InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value(), + FilePath()); + // In the event of a sporadic underlying failure, we might create a new file, + // but fail to update its mtime + atime. + PlatformFileError error = CreateFile(context, + context->src_origin_url(), context->src_type(), &file_info, 0, NULL); + if (base::PLATFORM_FILE_OK != error) + return error; + + return FileSystemFileUtil::GetInstance()->Touch(context, file_info.data_path, + last_access_time, last_modified_time); +} + +PlatformFileError ObfuscatedFileSystemFileUtil::Truncate( + FileSystemOperationContext* context, + const FilePath& virtual_path, + int64 length) { + FilePath local_path = + GetLocalPath(context->src_origin_url(), context->src_type(), + virtual_path); + if (local_path.empty()) + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + return FileSystemFileUtil::GetInstance()->Truncate( + context, local_path, length); +} + +bool ObfuscatedFileSystemFileUtil::PathExists( + FileSystemOperationContext* context, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return false; + FileId file_id; + return db->GetFileWithPath(virtual_path, &file_id); +} + +bool ObfuscatedFileSystemFileUtil::DirectoryExists( + FileSystemOperationContext* context, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return false; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return false; + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + NOTREACHED(); + return false; + } + return file_info.is_directory(); +} + +bool ObfuscatedFileSystemFileUtil::IsDirectoryEmpty( + FileSystemOperationContext* context, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return false; + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return true; // Not a great answer, but it's what others do. + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info)) { + DCHECK(!file_id); + // It's the root directory and the database hasn't been initialized yet. + return true; + } + if (!file_info.is_directory()) + return true; + std::vector<FileId> children; + // TODO(ericu): This could easily be made faster with help from the database. + if (!db->ListChildren(file_id, &children)) + return true; + return children.empty(); +} + +class ObfuscatedFileSystemFileEnumerator + : public FileSystemFileUtil::AbstractFileEnumerator { + public: + ObfuscatedFileSystemFileEnumerator( + FileSystemDirectoryDatabase* db, const FilePath& virtual_root_path) + : db_(db) { + FileId file_id; + FileInfo file_info; + if (!db_->GetFileWithPath(virtual_root_path, &file_id)) + return; + if (!db_->GetFileInfo(file_id, &file_info)) + return; + if (!file_info.is_directory()) + return; + FileRecord record = { file_id, file_info, virtual_root_path }; + display_queue_.push(record); + Next(); // Enumerators don't include the directory itself. + } + + ~ObfuscatedFileSystemFileEnumerator() {} + + virtual FilePath Next() { + ProcessRecurseQueue(); + if (display_queue_.empty()) + return FilePath(); + current_ = display_queue_.front(); + display_queue_.pop(); + if (current_.file_info.is_directory()) + recurse_queue_.push(current_); + return current_.file_path; + } + + virtual bool IsDirectory() { + return current_.file_info.is_directory(); + } + + private: + typedef FileSystemDirectoryDatabase::FileId FileId; + typedef FileSystemDirectoryDatabase::FileInfo FileInfo; + + struct FileRecord { + FileId file_id; + FileInfo file_info; + FilePath file_path; + }; + + void ProcessRecurseQueue() { + while (display_queue_.empty() && !recurse_queue_.empty()) { + FileRecord directory = recurse_queue_.front(); + std::vector<FileId> children; + recurse_queue_.pop(); + if (!db_->ListChildren(directory.file_id, &children)) + return; + std::vector<FileId>::iterator iter; + for (iter = children.begin(); iter != children.end(); ++iter) { + FileRecord child; + child.file_id = *iter; + if (!db_->GetFileInfo(child.file_id, &child.file_info)) + return; + FilePath::StringType name; +#if defined(OS_POSIX) + name = child.file_info.name; +#elif defined(OS_WIN) + name = base::SysUTF8ToWide(child.file_info.name); +#endif + child.file_path = directory.file_path.Append(name); + display_queue_.push(child); + } + } + } + + std::queue<FileRecord> display_queue_; + std::queue<FileRecord> recurse_queue_; + FileRecord current_; + FileSystemDirectoryDatabase* db_; +}; + +FileSystemFileUtil::AbstractFileEnumerator* +ObfuscatedFileSystemFileUtil::CreateFileEnumerator( + FileSystemOperationContext* context, + const FilePath& root_path) { + FileSystemDirectoryDatabase* db = + GetDirectoryDatabase(context->src_origin_url(), context->src_type()); + if (!db) + return new FileSystemFileUtil::EmptyFileEnumerator(); + return new ObfuscatedFileSystemFileEnumerator(db, root_path); +} + +PlatformFileError ObfuscatedFileSystemFileUtil::CreateFile( + FileSystemOperationContext* context, + const GURL& origin_url, FileSystemType type, + FileInfo* file_info, int file_flags, PlatformFile* handle) { + if (handle) + *handle = base::kInvalidPlatformFileValue; + FileSystemDirectoryDatabase* db = GetDirectoryDatabase(origin_url, type); + int64 number; + if (!db || !db->GetNextInteger(&number)) + return base::PLATFORM_FILE_ERROR_FAILED; + // We use the third- and fourth-to-last digits as the directory. + int64 directory_number = number % 10000 / 100; + FilePath path = + GetTopDir(origin_url, type).AppendASCII( + base::Int64ToString(directory_number)); + PlatformFileError error; + error = FileSystemFileUtil::GetInstance()->CreateDirectory( + context, path, false /* exclusive */, false /* recursive */); + if (base::PLATFORM_FILE_OK != error) + return error; + path = path.AppendASCII(base::Int64ToString(number)); + bool created = false; + if (!file_info->data_path.empty()) { + DCHECK(!file_flags); + DCHECK(!handle); + error = FileSystemFileUtil::GetInstance()->CopyOrMoveFile( + context, file_info->data_path, path, true /* copy */); + created = true; + } else { + if (handle) { + error = FileSystemFileUtil::GetInstance()->CreateOrOpen( + context, path, file_flags, handle, &created); + // If this succeeds, we must close handle on any subsequent error. + } else { + DCHECK(!file_flags); // file_flags is only used by CreateOrOpen. + error = FileSystemFileUtil::GetInstance()->EnsureFileExists( + context, path, &created); + } + } + if (error != base::PLATFORM_FILE_OK) + return error; + + if (!created) { + NOTREACHED(); + if (handle) { + base::ClosePlatformFile(*handle); + FileSystemFileUtil::GetInstance()->DeleteFile(context, path); + } + return base::PLATFORM_FILE_ERROR_FAILED; + } + file_info->data_path = path; + FileId file_id; + if (!db->AddFileInfo(*file_info, &file_id)) { + if (handle) + base::ClosePlatformFile(*handle); + FileSystemFileUtil::GetInstance()->DeleteFile(context, path); + return base::PLATFORM_FILE_ERROR_FAILED; + } + + return base::PLATFORM_FILE_OK; +} + +FilePath ObfuscatedFileSystemFileUtil::GetLocalPath( + const GURL& origin_url, + FileSystemType type, + const FilePath& virtual_path) { + FileSystemDirectoryDatabase* db = GetDirectoryDatabase(origin_url, type); + if (!db) + return FilePath(); + FileId file_id; + if (!db->GetFileWithPath(virtual_path, &file_id)) + return FilePath(); + FileInfo file_info; + if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { + NOTREACHED(); + return FilePath(); // Directories have no local path. + } + return file_info.data_path; +} + +FilePath ObfuscatedFileSystemFileUtil::GetTopDir( + const GURL& origin, FileSystemType type) { + // TODO: Is this easy to make backwards-compatible to look up old filesystems + // by info extracted from their directory names? + if (!origin_database_.get()) { + if (!file_util::CreateDirectory(file_system_directory_)) { + LOG(WARNING) << "Failed to create directory: " << + file_system_directory_.value(); + return FilePath(); + } + origin_database_.reset( + new FileSystemOriginDatabase( + file_system_directory_.AppendASCII(kOriginDatabaseName))); + } + FilePath directory_name; + if (!origin_database_->GetPathForOrigin(origin.spec(), &directory_name)) + return FilePath(); + std::string type_string = + FileSystemPathManager::GetFileSystemTypeString(type); + if (type_string.empty()) { + LOG(WARNING) << "Unknown filesystem type requested:" << type; + return FilePath(); + } + return file_system_directory_.Append(directory_name).AppendASCII(type_string); +} + +// TODO: How to do the whole validation-without-creation thing? We may not have +// quota even to create the database. +FileSystemDirectoryDatabase* ObfuscatedFileSystemFileUtil::GetDirectoryDatabase( + const GURL& origin, FileSystemType type) { + + std::string type_string = + FileSystemPathManager::GetFileSystemTypeString(type); + if (type_string.empty()) { + LOG(WARNING) << "Unknown filesystem type requested:" << type; + return NULL; + } + std::string key = origin.spec() + type_string; + DirectoryMap::iterator iter = directories_.find(key); + if (iter != directories_.end()) + return iter->second; + + FilePath path = GetTopDir(origin, type); + if (!file_util::DirectoryExists(path)) { + if (!file_util::CreateDirectory(path)) { + LOG(WARNING) << "Failed to create directory: " << path.value(); + return NULL; + } + } + path = path.AppendASCII(kDirectoryDatabaseName); + FileSystemDirectoryDatabase* database = new FileSystemDirectoryDatabase(path); + directories_[key] = database; + return database; +} + +void ObfuscatedFileSystemFileUtil::DropDatabases() { + origin_database_.reset(); + STLDeleteContainerPairSecondPointers( + directories_.begin(), directories_.end()); + directories_.clear(); +} + +} // namespace fileapi diff --git a/webkit/fileapi/obfuscated_file_system_file_util.h b/webkit/fileapi/obfuscated_file_system_file_util.h new file mode 100644 index 0000000..63e8712 --- /dev/null +++ b/webkit/fileapi/obfuscated_file_system_file_util.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_FILEAPI_OBFUSCATED_FILE_SYSTEM_FILE_UTIL_H_ +#define WEBKIT_FILEAPI_OBFUSCATED_FILE_SYSTEM_FILE_UTIL_H_ + +#include <map> +#include <vector> + +#include "base/file_path.h" +#include "base/file_util_proxy.h" +#include "base/platform_file.h" +#include "webkit/fileapi/file_system_directory_database.h" +#include "webkit/fileapi/file_system_file_util.h" +#include "webkit/fileapi/file_system_origin_database.h" +#include "webkit/fileapi/file_system_types.h" + +namespace base { +struct PlatformFileInfo; +class Time; +} + +class GURL; + +namespace fileapi { + +class FileSystemOperationContext; + +// The overall implementation philosophy of this class is that partial failures +// should leave us with an intact database; we'd prefer to leak the occasional +// backing file than have a database entry whose backing file is missing. When +// doing FSCK operations, if you find a loose backing file with no reference, +// you may safely delete it. +class ObfuscatedFileSystemFileUtil : public FileSystemFileUtil { + public: + + ObfuscatedFileSystemFileUtil(const FilePath& file_system_directory); + virtual ~ObfuscatedFileSystemFileUtil(); + + virtual base::PlatformFileError CreateOrOpen( + FileSystemOperationContext* context, + const FilePath& file_path, + int file_flags, + base::PlatformFile* file_handle, + bool* created) OVERRIDE; + + virtual base::PlatformFileError EnsureFileExists( + FileSystemOperationContext* context, + const FilePath& file_path, bool* created) OVERRIDE; + + virtual base::PlatformFileError GetLocalFilePath( + FileSystemOperationContext* context, + const FilePath& virtual_file, + FilePath* local_path) OVERRIDE; + + virtual base::PlatformFileError GetFileInfo( + FileSystemOperationContext* context, + const FilePath& file, + base::PlatformFileInfo* file_info, + FilePath* platform_file) OVERRIDE; + + virtual base::PlatformFileError ReadDirectory( + FileSystemOperationContext* context, + const FilePath& file_path, + std::vector<base::FileUtilProxy::Entry>* entries) OVERRIDE; + + virtual base::PlatformFileError CreateDirectory( + FileSystemOperationContext* context, + const FilePath& file_path, + bool exclusive, + bool recursive) OVERRIDE; + + virtual base::PlatformFileError CopyOrMoveFile( + FileSystemOperationContext* context, + const FilePath& src_file_path, + const FilePath& dest_file_path, + bool copy) OVERRIDE; + + virtual base::PlatformFileError DeleteFile( + FileSystemOperationContext* context, + const FilePath& file_path) OVERRIDE; + + virtual base::PlatformFileError DeleteSingleDirectory( + FileSystemOperationContext* context, + const FilePath& file_path) OVERRIDE; + + virtual base::PlatformFileError Touch( + FileSystemOperationContext* context, + const FilePath& file_path, + const base::Time& last_access_time, + const base::Time& last_modified_time) OVERRIDE; + + virtual base::PlatformFileError Truncate( + FileSystemOperationContext* context, + const FilePath& path, + int64 length) OVERRIDE; + + virtual bool PathExists( + FileSystemOperationContext* context, + const FilePath& file_path) OVERRIDE; + + virtual bool DirectoryExists( + FileSystemOperationContext* context, + const FilePath& file_path) OVERRIDE; + + virtual bool IsDirectoryEmpty( + FileSystemOperationContext* context, + const FilePath& file_path) OVERRIDE; + + protected: + virtual AbstractFileEnumerator* CreateFileEnumerator( + FileSystemOperationContext* context, + const FilePath& root_path) OVERRIDE; + + private: + typedef FileSystemDirectoryDatabase::FileId FileId; + typedef FileSystemDirectoryDatabase::FileInfo FileInfo; + + // Creates a new file, both the underlying backing file and the entry in the + // database. file_info is an in-out parameter. Supply the name and + // parent_id; supply data_path if you want it to be used as a source from + // which to COPY data--otherwise leave it empty. On success, data_path will + // always be set to the full path of a NEW backing file, and handle, if + // supplied, will hold open PlatformFile for the backing file, which the + // caller is responsible for closing. + // Caveat: do not supply handle if you're also supplying a data path. It was + // easier not to support this, and no code has needed it so far, so it will + // DCHECK and handle will hold base::kInvalidPlatformFileValue. + base::PlatformFileError CreateFile( + FileSystemOperationContext* context, + const GURL& origin_url, FileSystemType type, + FileInfo* file_info, + int file_flags, base::PlatformFile* handle); + // Given the filesystem's root URL and a virtual path, produces a real, full + // local path to the underlying data file. + FilePath GetLocalPath( + const GURL& origin_url, + FileSystemType type, + const FilePath& virtual_path); + // Gets the topmost directory specific to this origin and type. This will + // contain both the directory database's files and all the backing file + // subdirectories. If we decide to migrate in-place, without moving old files + // that were created by LocalFileSystemFileUtil, not all backing files will + // actually be in this directory. + FilePath GetTopDir(const GURL& origin, FileSystemType type); + FileSystemDirectoryDatabase* GetDirectoryDatabase( + const GURL& origin_url, FileSystemType type); + void DropDatabases(); + + typedef std::map<std::string, FileSystemDirectoryDatabase*> DirectoryMap; + DirectoryMap directories_; + scoped_ptr<FileSystemOriginDatabase> origin_database_; + FilePath file_system_directory_; + + DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileSystemFileUtil); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_OBFUSCATED_FILE_SYSTEM_FILE_UTIL_H_ diff --git a/webkit/fileapi/obfuscated_file_system_file_util_unittest.cc b/webkit/fileapi/obfuscated_file_system_file_util_unittest.cc new file mode 100644 index 0000000..07e0cd9 --- /dev/null +++ b/webkit/fileapi/obfuscated_file_system_file_util_unittest.cc @@ -0,0 +1,763 @@ +// Copyright (c) 2011 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 <set> +#include <string> + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_temp_dir.h" +#include "base/platform_file.h" +#include "base/sys_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/fileapi/obfuscated_file_system_file_util.h" + +using namespace fileapi; + +namespace { + +FilePath UTF8ToFilePath(const std::string& str) { + FilePath::StringType result; +#if defined(OS_POSIX) + result = str; +#elif defined(OS_WIN) + result = base::SysUTF8ToWide(str); +#endif + return FilePath(result); +} + +bool FileExists(const FilePath& path) { + return file_util::PathExists(path) && !file_util::DirectoryExists(path); +} + +// After a move, the dest exists and the source doesn't. +// After a copy, both source and dest exist. +struct CopyMoveTestCaseRecord { + bool is_copy_not_move; + const char source_path[64]; + const char dest_path[64]; + bool cause_overwrite; +}; + +const CopyMoveTestCaseRecord kCopyMoveTestCases[] = { + // This is the combinatoric set of: + // rename vs. same-name + // different directory vs. same directory + // overwrite vs. no-overwrite + // copy vs. move + // We can never be called with source and destination paths identical, so + // those cases are omitted. + {true, "dir0/file0", "dir0/file1", false}, + {false, "dir0/file0", "dir0/file1", false}, + {true, "dir0/file0", "dir0/file1", true}, + {false, "dir0/file0", "dir0/file1", true}, + + {true, "dir0/file0", "dir1/file0", false}, + {false, "dir0/file0", "dir1/file0", false}, + {true, "dir0/file0", "dir1/file0", true}, + {false, "dir0/file0", "dir1/file0", true}, + {true, "dir0/file0", "dir1/file1", false}, + {false, "dir0/file0", "dir1/file1", false}, + {true, "dir0/file0", "dir1/file1", true}, + {false, "dir0/file0", "dir1/file1", true}, +}; + +} // namespace (anonymous) + +// TODO(ericu): The vast majority of this and the other FSFU subclass tests +// could theoretically be shared. It would basically be a FSFU interface +// compliance test, and only the subclass-specific bits that look into the +// implementation would need to be written per-subclass. +class ObfuscatedFileSystemFileUtilTest : public testing::Test { + public: + ObfuscatedFileSystemFileUtilTest() { + } + + void SetUp() { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + + obfuscated_file_system_file_util_.reset( + new ObfuscatedFileSystemFileUtil(data_dir_.path())); + } + + FileSystemOperationContext* NewContext() { + FileSystemOperationContext *context = + new FileSystemOperationContext(NULL, NULL); + context->set_src_origin_url(GURL("http://example.com")); + context->set_dest_origin_url(GURL("http://example.com")); + context->set_src_type(kFileSystemTypeTemporary); + context->set_dest_type(kFileSystemTypeTemporary); + context->set_allowed_bytes_growth(1024 * 1024); + + return context; + } + + ObfuscatedFileSystemFileUtil* ofsfu() { + return obfuscated_file_system_file_util_.get(); + } + + int64 GetSize(const FilePath& path) { + int64 size; + EXPECT_TRUE(file_util::GetFileSize(path, &size)); + return size; + } + + void CheckFileAndCloseHandle( + const FilePath& virtual_path, PlatformFile file_handle) { + scoped_ptr<FileSystemOperationContext> context(NewContext()); + FilePath local_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetLocalFilePath( + context.get(), virtual_path, &local_path)); + + base::PlatformFileInfo file_info0; + FilePath data_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), virtual_path, &file_info0, &data_path)); + EXPECT_EQ(data_path, local_path); + EXPECT_TRUE(FileExists(data_path)); + EXPECT_EQ(0, GetSize(data_path)); + + const char data[] = "test data"; + const int length = arraysize(data) - 1; + + if (base::kInvalidPlatformFileValue == file_handle) { + bool created = true; + PlatformFileError error; + file_handle = base::CreatePlatformFile( + data_path, + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, + &created, + &error); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + EXPECT_FALSE(created); + } + ASSERT_EQ(length, base::WritePlatformFile(file_handle, 0, data, length)); + EXPECT_TRUE(base::ClosePlatformFile(file_handle)); + + base::PlatformFileInfo file_info1; + EXPECT_EQ(length, GetSize(data_path)); + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), virtual_path, &file_info1, &data_path)); + EXPECT_EQ(data_path, local_path); + + EXPECT_FALSE(file_info0.is_directory); + EXPECT_FALSE(file_info1.is_directory); + EXPECT_FALSE(file_info0.is_symbolic_link); + EXPECT_FALSE(file_info1.is_symbolic_link); + EXPECT_EQ(0, file_info0.size); + EXPECT_EQ(length, file_info1.size); + EXPECT_LE(file_info0.last_accessed, file_info1.last_accessed); + EXPECT_LE(file_info0.last_modified, file_info1.last_modified); + EXPECT_EQ(file_info0.creation_time, file_info1.creation_time); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->Truncate( + context.get(), virtual_path, length * 2)); + EXPECT_EQ(length * 2, GetSize(data_path)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->Truncate( + context.get(), virtual_path, 1)); + EXPECT_EQ(1, GetSize(data_path)); + } + + void ValidateTestDirectory( + const FilePath& root_path, + const std::set<std::string>& files, + const std::set<std::string>& directories) { + scoped_ptr<FileSystemOperationContext> context; + std::set<std::string>::const_iterator iter; + for (iter = files.begin(); iter != files.end(); ++iter) { + bool created = true; + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists( + context.get(), root_path.AppendASCII(*iter), + &created)); + ASSERT_FALSE(created); + } + for (iter = directories.begin(); iter != directories.end(); ++iter) { + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), + root_path.AppendASCII(*iter))); + } + } + + void FillTestDirectory( + const FilePath& root_path, + std::set<std::string>* files, + std::set<std::string>* directories) { + scoped_ptr<FileSystemOperationContext> context; + context.reset(NewContext()); + std::vector<base::FileUtilProxy::Entry> entries; + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->ReadDirectory(context.get(), root_path, &entries)); + EXPECT_EQ(0UL, entries.size()); + + files->clear(); + files->insert("first"); + files->insert("second"); + files->insert("third"); + directories->clear(); + directories->insert("fourth"); + directories->insert("fifth"); + directories->insert("sixth"); + std::set<std::string>::iterator iter; + for (iter = files->begin(); iter != files->end(); ++iter) { + bool created = false; + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists( + context.get(), root_path.AppendASCII(*iter), + &created)); + ASSERT_TRUE(created); + } + for (iter = directories->begin(); iter != directories->end(); ++iter) { + bool exclusive = true; + bool recursive = false; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->CreateDirectory( + context.get(), root_path.AppendASCII(*iter), + exclusive, recursive)); + } + ValidateTestDirectory(root_path, *files, *directories); + } + + void TestReadDirectoryHelper(const FilePath& root_path) { + std::set<std::string> files; + std::set<std::string> directories; + FillTestDirectory(root_path, &files, &directories); + + scoped_ptr<FileSystemOperationContext> context; + std::vector<base::FileUtilProxy::Entry> entries; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->ReadDirectory(context.get(), root_path, &entries)); + std::vector<base::FileUtilProxy::Entry>::iterator entry_iter; + EXPECT_EQ(files.size() + directories.size(), entries.size()); + for (entry_iter = entries.begin(); entry_iter != entries.end(); + ++entry_iter) { + const base::FileUtilProxy::Entry& entry = *entry_iter; + std::string name; + #if defined(OS_POSIX) + name = entry.name; + #elif defined(OS_WIN) + name = base::SysWideToUTF8(entry.name); + #endif + std::set<std::string>::iterator iter = files.find(name); + if (iter != files.end()) { + EXPECT_FALSE(entry.is_directory); + files.erase(iter); + continue; + } + iter = directories.find(name); + EXPECT_FALSE(directories.end() == iter); + EXPECT_TRUE(entry.is_directory); + directories.erase(iter); + } + } + + void TestTouchHelper(const FilePath& path) { + base::Time last_access_time = base::Time::Now(); // Ignored, so not tested. + base::Time last_modified_time = base::Time::Now(); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Touch( + context.get(), path, last_access_time, last_modified_time)); + FilePath local_path; + base::PlatformFileInfo file_info; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), path, &file_info, &local_path)); + // We compare as time_t here to lower our resolution, to avoid false + // negatives caused by conversion to the local filesystem's native + // representation and back. + EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); + + context.reset(NewContext()); + last_modified_time += base::TimeDelta::FromHours(1); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Touch( + context.get(), path, last_access_time, last_modified_time)); + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), path, &file_info, &local_path)); + EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); + } + + private: + ScopedTempDir data_dir_; + scoped_ptr<ObfuscatedFileSystemFileUtil> obfuscated_file_system_file_util_; + + DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileSystemFileUtilTest); +}; + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestCreateAndDeleteFile) { + base::PlatformFile file_handle = base::kInvalidPlatformFileValue; + bool created; + FilePath path = UTF8ToFilePath("fake/file"); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; + + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->CreateOrOpen( + context.get(), path, file_flags, &file_handle, + &created)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->DeleteFile(context.get(), path)); + + path = UTF8ToFilePath("test file"); + + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->CreateOrOpen( + context.get(), path, file_flags, &file_handle, &created)); + ASSERT_TRUE(created); + EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); + + CheckFileAndCloseHandle(path, file_handle); + + context.reset(NewContext()); + FilePath local_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetLocalFilePath( + context.get(), path, &local_path)); + EXPECT_TRUE(file_util::PathExists(local_path)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->DeleteFile(context.get(), path)); + EXPECT_FALSE(file_util::PathExists(local_path)); + + context.reset(NewContext()); + bool exclusive = true; + bool recursive = true; + FilePath directory_path = UTF8ToFilePath("series/of/directories"); + path = directory_path.AppendASCII("file name"); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), directory_path, exclusive, recursive)); + + context.reset(NewContext()); + file_handle = base::kInvalidPlatformFileValue; + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->CreateOrOpen( + context.get(), path, file_flags, &file_handle, &created)); + ASSERT_TRUE(created); + EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); + + CheckFileAndCloseHandle(path, file_handle); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetLocalFilePath( + context.get(), path, &local_path)); + EXPECT_TRUE(file_util::PathExists(local_path)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->DeleteFile(context.get(), path)); + EXPECT_FALSE(file_util::PathExists(local_path)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestTruncate) { + bool created = false; + FilePath path = UTF8ToFilePath("file"); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->Truncate(context.get(), path, 4)); + + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), path, &created)); + ASSERT_TRUE(created); + + context.reset(NewContext()); + FilePath local_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetLocalFilePath( + context.get(), path, &local_path)); + EXPECT_EQ(0, GetSize(local_path)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->Truncate( + context.get(), path, 10)); + EXPECT_EQ(10, GetSize(local_path)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->Truncate( + context.get(), path, 1)); + EXPECT_EQ(1, GetSize(local_path)); + + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->PathExists(context.get(), path)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestEnsureFileExists) { + FilePath path = UTF8ToFilePath("fake/file"); + bool created = false; + scoped_ptr<FileSystemOperationContext> context(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->EnsureFileExists( + context.get(), path, &created)); + + context.reset(NewContext()); + path = UTF8ToFilePath("test file"); + created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), path, &created)); + ASSERT_TRUE(created); + + CheckFileAndCloseHandle(path, base::kInvalidPlatformFileValue); + + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), path, &created)); + ASSERT_FALSE(created); + + // Also test in a subdirectory. + path = UTF8ToFilePath("path/to/file.txt"); + context.reset(NewContext()); + bool exclusive = true; + bool recursive = true; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path.DirName(), exclusive, recursive)); + + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), path, &created)); + ASSERT_TRUE(created); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->PathExists(context.get(), path)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestDirectoryOps) { + scoped_ptr<FileSystemOperationContext> context(NewContext()); + + bool exclusive = false; + bool recursive = false; + FilePath path = UTF8ToFilePath("foo/bar"); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->DeleteSingleDirectory(context.get(), path)); + + FilePath root = UTF8ToFilePath(""); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->PathExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->IsDirectoryEmpty(context.get(), root)); + + context.reset(NewContext()); + exclusive = false; + recursive = true; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->PathExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->IsDirectoryEmpty(context.get(), root)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), path.DirName())); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->IsDirectoryEmpty(context.get(), path.DirName())); + + // Can't remove a non-empty directory. + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_FAILED, + ofsfu()->DeleteSingleDirectory(context.get(), path.DirName())); + + base::PlatformFileInfo file_info; + FilePath local_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), path, &file_info, &local_path)); + EXPECT_TRUE(local_path.empty()); + EXPECT_TRUE(file_info.is_directory); + EXPECT_FALSE(file_info.is_symbolic_link); + + // Same create again should succeed, since exclusive is false. + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + exclusive = true; + recursive = true; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->DeleteSingleDirectory(context.get(), path)); + + path = UTF8ToFilePath("foo/bop"); + + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->PathExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->IsDirectoryEmpty(context.get(), path)); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofsfu()->GetFileInfo( + context.get(), path, &file_info, &local_path)); + + exclusive = true; + recursive = false; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->PathExists(context.get(), path)); + + exclusive = true; + recursive = false; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + exclusive = true; + recursive = false; + path = UTF8ToFilePath("foo"); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + path = UTF8ToFilePath("blah"); + + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->PathExists(context.get(), path)); + + exclusive = true; + recursive = false; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->PathExists(context.get(), path)); + + exclusive = true; + recursive = false; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestReadDirectory) { + scoped_ptr<FileSystemOperationContext> context(NewContext()); + bool exclusive = true; + bool recursive = true; + FilePath path = UTF8ToFilePath("directory/to/use"); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + TestReadDirectoryHelper(path); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestReadRootWithSlash) { + TestReadDirectoryHelper(UTF8ToFilePath("")); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestReadRootWithEmptyString) { + TestReadDirectoryHelper(UTF8ToFilePath("/")); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestReadDirectoryOnFile) { + FilePath path = UTF8ToFilePath("file"); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + + bool created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), path, &created)); + ASSERT_TRUE(created); + + context.reset(NewContext()); + std::vector<base::FileUtilProxy::Entry> entries; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->ReadDirectory(context.get(), path, &entries)); + + EXPECT_TRUE(ofsfu()->IsDirectoryEmpty(context.get(), path)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestTouch) { + FilePath path = UTF8ToFilePath("fake/file"); + base::Time last_access_time = base::Time::Now(); // Ignored, so not tested. + base::Time last_modified_time = base::Time::Now(); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->Touch( + context.get(), path, last_access_time, last_modified_time)); + + // Touch will create a file if it's not there but its parent is. + path = UTF8ToFilePath("file name"); + TestTouchHelper(path); + + bool exclusive = true; + bool recursive = true; + path = UTF8ToFilePath("directory/to/use"); + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), path, exclusive, recursive)); + TestTouchHelper(path); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestCopyOrMoveFileNotFound) { + FilePath source_path = UTF8ToFilePath("path0.txt"); + FilePath dest_path = UTF8ToFilePath("path1.txt"); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + + bool is_copy_not_move = false; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->CopyOrMoveFile(context.get(), source_path, dest_path, + is_copy_not_move)); + context.reset(NewContext()); + is_copy_not_move = true; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->CopyOrMoveFile(context.get(), source_path, dest_path, + is_copy_not_move)); + source_path = UTF8ToFilePath("dir/dir/file"); + bool exclusive = true; + bool recursive = true; + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), source_path.DirName(), exclusive, recursive)); + is_copy_not_move = false; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->CopyOrMoveFile(context.get(), source_path, dest_path, + is_copy_not_move)); + context.reset(NewContext()); + is_copy_not_move = true; + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, + ofsfu()->CopyOrMoveFile(context.get(), source_path, dest_path, + is_copy_not_move)); +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestCopyOrMoveFileSuccess) { + const int64 kSourceLength = 5; + const int64 kDestLength = 50; + + for (size_t i = 0; i < arraysize(kCopyMoveTestCases); ++i) { + SCOPED_TRACE(testing::Message() << "kCopyMoveTestCase " << i); + const CopyMoveTestCaseRecord& test_case = kCopyMoveTestCases[i]; + SCOPED_TRACE(testing::Message() << "\t is_copy_not_move " << + test_case.is_copy_not_move); + SCOPED_TRACE(testing::Message() << "\t source_path " << + test_case.source_path); + SCOPED_TRACE(testing::Message() << "\t dest_path " << + test_case.dest_path); + SCOPED_TRACE(testing::Message() << "\t cause_overwrite " << + test_case.cause_overwrite); + scoped_ptr<FileSystemOperationContext> context(NewContext()); + + bool exclusive = false; + bool recursive = true; + FilePath source_path = UTF8ToFilePath(test_case.source_path); + FilePath dest_path = UTF8ToFilePath(test_case.dest_path); + + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), source_path.DirName(), exclusive, recursive)); + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), dest_path.DirName(), exclusive, recursive)); + + base::Time last_access_time; + bool created = false; + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), source_path, &created)); + ASSERT_TRUE(created); + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Truncate(context.get(), source_path, kSourceLength)); + + if (test_case.cause_overwrite) { + context.reset(NewContext()); + created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->EnsureFileExists(context.get(), dest_path, &created)); + ASSERT_TRUE(created); + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Truncate(context.get(), dest_path, kDestLength)); + } + + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CopyOrMoveFile(context.get(), + source_path, dest_path, test_case.is_copy_not_move)); + if (test_case.is_copy_not_move) { + base::PlatformFileInfo file_info; + FilePath local_path; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), source_path, &file_info, &local_path)); + EXPECT_EQ(kSourceLength, file_info.size); + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->DeleteFile(context.get(), source_path)); + } else { + base::PlatformFileInfo file_info; + FilePath local_path; + context.reset(NewContext()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofsfu()->GetFileInfo( + context.get(), source_path, &file_info, &local_path)); + } + base::PlatformFileInfo file_info; + FilePath local_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, ofsfu()->GetFileInfo( + context.get(), dest_path, &file_info, &local_path)); + EXPECT_EQ(kSourceLength, file_info.size); + + EXPECT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->DeleteFile(context.get(), dest_path)); + } +} + +TEST_F(ObfuscatedFileSystemFileUtilTest, TestEnumerator) { + scoped_ptr<FileSystemOperationContext> context(NewContext()); + FilePath src_path = UTF8ToFilePath("source dir"); + bool exclusive = true; + bool recursive = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, ofsfu()->CreateDirectory( + context.get(), src_path, exclusive, recursive)); + + std::set<std::string> files; + std::set<std::string> directories; + FillTestDirectory(src_path, &files, &directories); + + FilePath dest_path = UTF8ToFilePath("destination dir"); + + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), dest_path)); + context.reset(NewContext()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Copy(context.get(), src_path, dest_path)); + + ValidateTestDirectory(dest_path, files, directories); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), src_path)); + context.reset(NewContext()); + EXPECT_TRUE(ofsfu()->DirectoryExists(context.get(), dest_path)); + context.reset(NewContext()); + recursive = true; + ASSERT_EQ(base::PLATFORM_FILE_OK, + ofsfu()->Delete(context.get(), dest_path, recursive)); + context.reset(NewContext()); + EXPECT_FALSE(ofsfu()->DirectoryExists(context.get(), dest_path)); +} diff --git a/webkit/fileapi/sandbox_mount_point_provider.cc b/webkit/fileapi/sandbox_mount_point_provider.cc index ad46f3b..d344e63 100644 --- a/webkit/fileapi/sandbox_mount_point_provider.cc +++ b/webkit/fileapi/sandbox_mount_point_provider.cc @@ -170,10 +170,13 @@ SandboxMountPointProvider::SandboxMountPointProvider( const FilePath& profile_path) : path_manager_(path_manager), file_message_loop_(file_message_loop), - base_path_(profile_path.Append(kFileSystemDirectory)) { + base_path_(profile_path.Append(kFileSystemDirectory)), + sandbox_file_util_(new ObfuscatedFileSystemFileUtil(base_path_)) { } SandboxMountPointProvider::~SandboxMountPointProvider() { + if (!file_message_loop_->BelongsToCurrentThread()) + file_message_loop_->DeleteSoon(FROM_HERE, sandbox_file_util_.release()); } bool SandboxMountPointProvider::IsAccessAllowed(const GURL& origin_url, diff --git a/webkit/fileapi/sandbox_mount_point_provider.h b/webkit/fileapi/sandbox_mount_point_provider.h index 748ced8..071a6fa 100644 --- a/webkit/fileapi/sandbox_mount_point_provider.h +++ b/webkit/fileapi/sandbox_mount_point_provider.h @@ -9,8 +9,10 @@ #include <vector> #include "base/file_path.h" +#include "base/memory/scoped_ptr.h" #include "googleurl/src/gurl.h" #include "webkit/fileapi/file_system_mount_point_provider.h" +#include "webkit/fileapi/obfuscated_file_system_file_util.h" namespace base { class MessageLoopProxy; @@ -94,6 +96,10 @@ class SandboxMountPointProvider : public FileSystemMountPointProvider { const GURL& origin_url, fileapi::FileSystemType type) const; + ObfuscatedFileSystemFileUtil* sandbox_file_util() { + return sandbox_file_util_.get(); + } + private: bool GetOriginBasePathAndName( const GURL& origin_url, @@ -111,6 +117,8 @@ class SandboxMountPointProvider : public FileSystemMountPointProvider { const FilePath base_path_; + scoped_ptr<ObfuscatedFileSystemFileUtil> sandbox_file_util_; + DISALLOW_COPY_AND_ASSIGN(SandboxMountPointProvider); }; diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index aa70b0b..6f03750 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -50,6 +50,8 @@ 'file_writer_delegate.h', 'local_file_system_file_util.cc', 'local_file_system_file_util.h', + 'obfuscated_file_system_file_util.cc', + 'obfuscated_file_system_file_util.h', 'quota_file_util.cc', 'quota_file_util.h', 'sandbox_mount_point_provider.cc', diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index d289200..e2ea35b 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -385,9 +385,9 @@ '../../fileapi/file_system_usage_cache_unittest.cc', '../../fileapi/file_system_util_unittest.cc', '../../fileapi/local_file_system_file_util_unittest.cc', + '../../fileapi/obfuscated_file_system_file_util_unittest.cc', '../../fileapi/quota_file_util_unittest.cc', '../../fileapi/sandbox_mount_point_provider_unittest.cc', - '../../fileapi/sandbox_mount_point_provider_unittest.cc', '../../fileapi/sandbox_quota_client_unittest.cc', '../../fileapi/webfilewriter_base_unittest.cc', '../../glue/bookmarklet_unittest.cc', |