summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/platform_file.h7
-rw-r--r--base/platform_file_posix.cc10
-rw-r--r--base/platform_file_win.cc4
-rw-r--r--content/content_browser.gypi2
-rw-r--r--webkit/fileapi/file_system_directory_database.cc57
-rw-r--r--webkit/fileapi/file_system_directory_database.h11
-rw-r--r--webkit/fileapi/file_system_directory_database_unittest.cc4
-rw-r--r--webkit/fileapi/file_system_file_util.cc3
-rw-r--r--webkit/fileapi/file_system_file_util.h3
-rw-r--r--webkit/fileapi/obfuscated_file_system_file_util.cc766
-rw-r--r--webkit/fileapi/obfuscated_file_system_file_util.h161
-rw-r--r--webkit/fileapi/obfuscated_file_system_file_util_unittest.cc763
-rw-r--r--webkit/fileapi/sandbox_mount_point_provider.cc5
-rw-r--r--webkit/fileapi/sandbox_mount_point_provider.h8
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi2
-rw-r--r--webkit/tools/test_shell/test_shell.gypi2
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',