diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-02 09:35:05 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-02 09:35:05 +0000 |
commit | 6fe6965e2d0e4ac248d6825a88b5e53a77ac5ffe (patch) | |
tree | 674002b384f19261c6bf00c1033954b7a771345c | |
parent | b93a8aea9cc770f414fdfad8fad7dcae432eb177 (diff) | |
download | chromium_src-6fe6965e2d0e4ac248d6825a88b5e53a77ac5ffe.zip chromium_src-6fe6965e2d0e4ac248d6825a88b5e53a77ac5ffe.tar.gz chromium_src-6fe6965e2d0e4ac248d6825a88b5e53a77ac5ffe.tar.bz2 |
Add isolated file_util for directory (and file) drag-and-drop support.
patch from http://codereview.chromium.org/9204009/
BUG=99823
TEST=test_shell_tests:IsolatedFileUtil*
Review URL: https://chromiumcodereview.appspot.com/9272007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@130110 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | webkit/fileapi/file_system_context.cc | 6 | ||||
-rw-r--r-- | webkit/fileapi/file_system_context.h | 2 | ||||
-rw-r--r-- | webkit/fileapi/file_system_operation.h | 3 | ||||
-rw-r--r-- | webkit/fileapi/file_system_types.h | 3 | ||||
-rw-r--r-- | webkit/fileapi/file_system_util.cc | 49 | ||||
-rw-r--r-- | webkit/fileapi/file_system_util.h | 2 | ||||
-rw-r--r-- | webkit/fileapi/isolated_context.cc | 4 | ||||
-rw-r--r-- | webkit/fileapi/isolated_context.h | 18 | ||||
-rw-r--r-- | webkit/fileapi/isolated_context_unittest.cc | 20 | ||||
-rw-r--r-- | webkit/fileapi/isolated_file_util.cc | 388 | ||||
-rw-r--r-- | webkit/fileapi/isolated_file_util.h | 98 | ||||
-rw-r--r-- | webkit/fileapi/isolated_file_util_unittest.cc | 432 | ||||
-rw-r--r-- | webkit/fileapi/isolated_mount_point_provider.cc | 102 | ||||
-rw-r--r-- | webkit/fileapi/isolated_mount_point_provider.h | 57 | ||||
-rw-r--r-- | webkit/fileapi/test_file_set.cc | 31 | ||||
-rw-r--r-- | webkit/fileapi/webkit_fileapi.gypi | 4 | ||||
-rw-r--r-- | webkit/glue/webdropdata.cc | 3 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 1 |
18 files changed, 1172 insertions, 51 deletions
diff --git a/webkit/fileapi/file_system_context.cc b/webkit/fileapi/file_system_context.cc index 600518e..07acff2 100644 --- a/webkit/fileapi/file_system_context.cc +++ b/webkit/fileapi/file_system_context.cc @@ -13,6 +13,7 @@ #include "webkit/fileapi/file_system_options.h" #include "webkit/fileapi/file_system_quota_client.h" #include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/isolated_mount_point_provider.h" #include "webkit/fileapi/sandbox_mount_point_provider.h" #include "webkit/quota/quota_manager.h" #include "webkit/quota/special_storage_policy.h" @@ -57,7 +58,8 @@ FileSystemContext::FileSystemContext( new SandboxMountPointProvider( file_message_loop, profile_path, - options)) { + options)), + isolated_provider_(new IsolatedMountPointProvider) { if (quota_manager_proxy) { quota_manager_proxy->RegisterClient(CreateQuotaClient( file_message_loop, this, options.is_incognito())); @@ -123,6 +125,8 @@ FileSystemMountPointProvider* FileSystemContext::GetMountPointProvider( return sandbox_provider_.get(); case kFileSystemTypeExternal: return external_provider_.get(); + case kFileSystemTypeIsolated: + return isolated_provider_.get(); case kFileSystemTypeUnknown: default: NOTREACHED(); diff --git a/webkit/fileapi/file_system_context.h b/webkit/fileapi/file_system_context.h index 2a6b211..7824e9d 100644 --- a/webkit/fileapi/file_system_context.h +++ b/webkit/fileapi/file_system_context.h @@ -32,6 +32,7 @@ class FileSystemOperationInterface; class FileSystemOptions; class FileSystemPathManager; class FileSystemQuotaUtil; +class IsolatedMountPointProvider; class SandboxMountPointProvider; struct DefaultContextDeleter; @@ -122,6 +123,7 @@ class FileSystemContext // Mount point providers. scoped_ptr<SandboxMountPointProvider> sandbox_provider_; + scoped_ptr<IsolatedMountPointProvider> isolated_provider_; scoped_ptr<ExternalFileSystemMountPointProvider> external_provider_; DISALLOW_IMPLICIT_CONSTRUCTORS(FileSystemContext); diff --git a/webkit/fileapi/file_system_operation.h b/webkit/fileapi/file_system_operation.h index 0bf87cd..380e3ee 100644 --- a/webkit/fileapi/file_system_operation.h +++ b/webkit/fileapi/file_system_operation.h @@ -119,8 +119,9 @@ class FileSystemOperation : public FileSystemOperationInterface { // Only MountPointProviders or testing class can create a // new operation directly. - friend class SandboxMountPointProvider; friend class FileSystemTestHelper; + friend class IsolatedMountPointProvider; + friend class SandboxMountPointProvider; friend class chromeos::CrosMountPointProvider; friend class FileSystemOperationTest; diff --git a/webkit/fileapi/file_system_types.h b/webkit/fileapi/file_system_types.h index b7c96f1..b48d746 100644 --- a/webkit/fileapi/file_system_types.h +++ b/webkit/fileapi/file_system_types.h @@ -23,6 +23,9 @@ enum FileSystemType { kFileSystemTypeTemporary = WebKit::WebFileSystem::TypeTemporary, kFileSystemTypePersistent = WebKit::WebFileSystem::TypePersistent, + // Indicates non-sandboxed isolated filesystem. + kFileSystemTypeIsolated = WebKit::WebFileSystem::TypeIsolated, + // Indicates non-sandboxed filesystem where files are placed outside the // profile directory (thus called 'external' filesystem). // This filesystem is used only by Chrome OS as of writing. diff --git a/webkit/fileapi/file_system_util.cc b/webkit/fileapi/file_system_util.cc index ab145f1..ed365c6 100644 --- a/webkit/fileapi/file_system_util.cc +++ b/webkit/fileapi/file_system_util.cc @@ -8,6 +8,7 @@ #include "base/file_path.h" #include "base/logging.h" +#include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "googleurl/src/gurl.h" @@ -21,16 +22,18 @@ namespace fileapi { const char kPersistentDir[] = "/persistent/"; const char kTemporaryDir[] = "/temporary/"; +const char kIsolatedDir[] = "/isolated/"; const char kExternalDir[] = "/external/"; const char kPersistentName[] = "Persistent"; const char kTemporaryName[] = "Temporary"; +const char kIsolatedName[] = "Isolated"; const char kExternalName[] = "External"; bool CrackFileSystemURL(const GURL& url, GURL* origin_url, FileSystemType* type, FilePath* file_path) { GURL origin; - FileSystemType file_system_type; + FileSystemType file_system_type = kFileSystemTypeUnknown; if (url.scheme() != "filesystem") return false; @@ -64,19 +67,27 @@ bool CrackFileSystemURL(const GURL& url, GURL* origin_url, FileSystemType* type, std::string path = net::UnescapeURLComponent(bare_url.path(), net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS); - if (path.compare(0, strlen(kPersistentDir), kPersistentDir) == 0) { - file_system_type = kFileSystemTypePersistent; - path = path.substr(strlen(kPersistentDir)); - } else if (path.compare(0, strlen(kTemporaryDir), kTemporaryDir) == 0) { - file_system_type = kFileSystemTypeTemporary; - path = path.substr(strlen(kTemporaryDir)); - } else if (path.compare(0, strlen(kExternalDir), kExternalDir) == 0) { - file_system_type = kFileSystemTypeExternal; - path = path.substr(strlen(kExternalDir)); - } else { - return false; + + const struct { + FileSystemType type; + const char* dir; + } kValidTypes[] = { + { kFileSystemTypePersistent, kPersistentDir }, + { kFileSystemTypeTemporary, kTemporaryDir }, + { kFileSystemTypeIsolated, kIsolatedDir }, + { kFileSystemTypeExternal, kExternalDir }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidTypes); ++i) { + if (StartsWithASCII(path, kValidTypes[i].dir, true)) { + file_system_type = kValidTypes[i].type; + path = path.substr(strlen(kValidTypes[i].dir)); + break; + } } + if (file_system_type == kFileSystemTypeUnknown) + return false; + // Ensure the path is relative. while (!path.empty() && path[0] == '/') path.erase(0, 1); @@ -143,18 +154,20 @@ GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) { switch (type) { case kFileSystemTypeTemporary: path += (kTemporaryDir + 1); // We don't want the leading slash. - break; + return GURL(path); case kFileSystemTypePersistent: path += (kPersistentDir + 1); // We don't want the leading slash. - break; + return GURL(path); case kFileSystemTypeExternal: path += (kExternalDir + 1); // We don't want the leading slash. - break; - default: + return GURL(path); + case kFileSystemTypeIsolated: + // Falling through; we won't call this for isolated filesystems. + case kFileSystemTypeUnknown: NOTREACHED(); - return GURL(); } - return GURL(path); + NOTREACHED(); + return GURL(); } std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) { diff --git a/webkit/fileapi/file_system_util.h b/webkit/fileapi/file_system_util.h index 730cc55..475565f 100644 --- a/webkit/fileapi/file_system_util.h +++ b/webkit/fileapi/file_system_util.h @@ -17,9 +17,11 @@ namespace fileapi { extern const char kPersistentDir[]; extern const char kTemporaryDir[]; extern const char kExternalDir[]; +extern const char kIsolatedDir[]; extern const char kPersistentName[]; extern const char kTemporaryName[]; extern const char kExternalName[]; +extern const char kIsolatedName[]; // Cracks the given filesystem |url| and populates |origin_url|, |type| // and |file_path|. Returns true if the given |url| is a valid filesystem diff --git a/webkit/fileapi/isolated_context.cc b/webkit/fileapi/isolated_context.cc index 64a3a02..61133f6 100644 --- a/webkit/fileapi/isolated_context.cc +++ b/webkit/fileapi/isolated_context.cc @@ -55,6 +55,7 @@ void IsolatedContext::RevokeIsolatedFileSystem( bool IsolatedContext::CrackIsolatedPath(const FilePath& virtual_path, std::string* filesystem_id, + FilePath* root_path, FilePath* platform_path) const { DCHECK(filesystem_id); DCHECK(platform_path); @@ -87,6 +88,8 @@ bool IsolatedContext::CrackIsolatedPath(const FilePath& virtual_path, if (found == found_toplevels->second.end()) return false; FilePath path = found->second; + if (root_path) + *root_path = path; for (size_t i = 2; i < components.size(); ++i) { path = path.Append(components[i]); } @@ -97,6 +100,7 @@ bool IsolatedContext::CrackIsolatedPath(const FilePath& virtual_path, bool IsolatedContext::GetTopLevelPaths(std::string filesystem_id, std::vector<FilePath>* paths) const { DCHECK(paths); + base::AutoLock locker(lock_); IDToPathMap::const_iterator found = toplevel_map_.find(filesystem_id); if (found == toplevel_map_.end()) return false; diff --git a/webkit/fileapi/isolated_context.h b/webkit/fileapi/isolated_context.h index e9233c3..de4afda 100644 --- a/webkit/fileapi/isolated_context.h +++ b/webkit/fileapi/isolated_context.h @@ -56,14 +56,20 @@ class IsolatedContext { // Cracks the given |virtual_path| (which should look like // "/<filesystem_id>/<relative_path>") and populates the |filesystem_id| - // and |platform_path| if the embedded <filesystem_id> is registered - // to this context. + // and |platform_path| if the embedded <filesystem_id> is registerred + // to this context. |root_path| is also populated to have the platform + // root (toplevel) path for the |virtual_path| + // (i.e. |platform_path| = |root_path| + <relative_path>). + // // Returns false if the given virtual_path or the cracked filesystem_id // is not valid. - // Note that |platform_path| is set to an empty path if |virtual_path| has no - // <relative_path> part (i.e. pointing to the virtual root). + // + // Note that |root_path| and |platform_path| are set to empty paths if + // |virtual_path| has no <relative_path> part (i.e. pointing to + // the virtual root). bool CrackIsolatedPath(const FilePath& virtual_path, std::string* filesystem_id, + FilePath* root_path, FilePath* platform_path) const; // Returns a vector of the full paths of the top-level entry paths @@ -73,8 +79,6 @@ class IsolatedContext { std::vector<FilePath>* paths) const; // Returns the virtual path that looks like /<filesystem_id>/<relative_path>. - // This method is only used by the testing code (as the actual virtual path - // in the real code is created in the renderer side). FilePath CreateVirtualPath(const std::string& filesystem_id, const FilePath& relative_path) const; @@ -92,7 +96,7 @@ class IsolatedContext { // Returns a new filesystem_id. Called with lock. std::string GetNewFileSystemId() const; - // This lock needs to be obtained when accessing the fileset_. + // This lock needs to be obtained when accessing the toplevel_map_. mutable base::Lock lock_; // Maps the toplevel entries to the filesystem id. diff --git a/webkit/fileapi/isolated_context_unittest.cc b/webkit/fileapi/isolated_context_unittest.cc index e32adef..5a828f5 100644 --- a/webkit/fileapi/isolated_context_unittest.cc +++ b/webkit/fileapi/isolated_context_unittest.cc @@ -77,11 +77,13 @@ TEST_F(IsolatedContextTest, RegisterAndRevokeTest) { FilePath virtual_path = isolated_context()->CreateVirtualPath( id_, kTestPaths[i].BaseName()); std::string cracked_id; - FilePath cracked_path; + FilePath root_path, cracked_path; ASSERT_TRUE(isolated_context()->CrackIsolatedPath( - virtual_path, &cracked_id, &cracked_path)); + virtual_path, &cracked_id, &root_path, &cracked_path)); ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(), cracked_path.value()); + ASSERT_TRUE(fileset_.find(root_path.NormalizePathSeparators()) + != fileset_.end()); ASSERT_EQ(id_, cracked_id); } @@ -123,14 +125,16 @@ TEST_F(IsolatedContextTest, CrackWithRelativePaths) { FilePath virtual_path = isolated_context()->CreateVirtualPath( id_, kTestPaths[i].BaseName().Append(relatives[j].path)); std::string cracked_id; - FilePath cracked_path; + FilePath root_path, cracked_path; if (!relatives[j].valid) { ASSERT_FALSE(isolated_context()->CrackIsolatedPath( - virtual_path, &cracked_id, &cracked_path)); + virtual_path, &cracked_id, &root_path, &cracked_path)); continue; } ASSERT_TRUE(isolated_context()->CrackIsolatedPath( - virtual_path, &cracked_id, &cracked_path)); + virtual_path, &cracked_id, &root_path, &cracked_path)); + ASSERT_TRUE(fileset_.find(root_path.NormalizePathSeparators()) + != fileset_.end()); ASSERT_EQ(kTestPaths[i].Append(relatives[j].path) .NormalizePathSeparators().value(), cracked_path.value()); @@ -141,7 +145,7 @@ TEST_F(IsolatedContextTest, CrackWithRelativePaths) { TEST_F(IsolatedContextTest, TestWithVirtualRoot) { std::string cracked_id; - FilePath cracked_path; + FilePath root_path, cracked_path; const FilePath root(FPL("/")); // Trying to crack virtual root "/" returns true but with empty cracked path @@ -149,7 +153,7 @@ TEST_F(IsolatedContextTest, TestWithVirtualRoot) { // that has no corresponding platform directory. FilePath virtual_path = isolated_context()->CreateVirtualPath(id_, root); ASSERT_TRUE(isolated_context()->CrackIsolatedPath( - virtual_path, &cracked_id, &cracked_path)); + virtual_path, &cracked_id, &root_path, &cracked_path)); ASSERT_EQ(FPL(""), cracked_path.value()); ASSERT_EQ(id_, cracked_id); @@ -158,7 +162,7 @@ TEST_F(IsolatedContextTest, TestWithVirtualRoot) { virtual_path = isolated_context()->CreateVirtualPath( id_, FilePath(FPL("foo"))); ASSERT_FALSE(isolated_context()->CrackIsolatedPath( - virtual_path, &cracked_id, &cracked_path)); + virtual_path, &cracked_id, &root_path, &cracked_path)); } } // namespace fileapi diff --git a/webkit/fileapi/isolated_file_util.cc b/webkit/fileapi/isolated_file_util.cc new file mode 100644 index 0000000..f699135 --- /dev/null +++ b/webkit/fileapi/isolated_file_util.cc @@ -0,0 +1,388 @@ +// Copyright (c) 2012 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/isolated_file_util.h" + +#include "base/memory/scoped_ptr.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/fileapi/file_system_path.h" +#include "webkit/fileapi/isolated_context.h" + +using base::PlatformFileError; +using base::PlatformFileInfo; + +namespace fileapi { + +namespace { + +// Simply enumerate each path from a given paths set. +// Used to enumerate top-level paths of an isolated filesystem. +class SetFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator { + public: + SetFileEnumerator(FileSystemFileUtil* underlying_file_util, + const FileSystemOperationContext& context, + const std::vector<FilePath>& paths, + const FileSystemPath& root) + : underlying_file_util_(underlying_file_util), + context_(context), + paths_(paths), + root_(root) { + DCHECK(underlying_file_util_); + path_iter_ = paths_.begin(); + } + virtual ~SetFileEnumerator() {} + + // AbstractFileEnumerator overrides. + virtual FilePath Next() OVERRIDE { + if (path_iter_ == paths_.end()) + return FilePath(); + FilePath platform_path; + underlying_file_util_->GetFileInfo( + &context_, root_.WithInternalPath(*path_iter_++), + &file_info_, &platform_path); + return root_.internal_path().Append(platform_path.BaseName()); + } + virtual int64 Size() OVERRIDE { return file_info_.size; } + virtual bool IsDirectory() OVERRIDE { return file_info_.is_directory; } + virtual base::Time LastModifiedTime() OVERRIDE { + return file_info_.last_modified; + } + virtual bool IsLink() OVERRIDE { return file_info_.is_symbolic_link; } + + private: + // Not owned; Enumerator is instantiated only within methods of + // FileSystemFileUtil so it must be ok to assume the underlying_file_util + // is alive throughout the lifetime of the enumerator. + FileSystemFileUtil* underlying_file_util_; + + FileSystemOperationContext context_; + std::vector<FilePath> paths_; + std::vector<FilePath>::const_iterator path_iter_; + FileSystemPath root_; + base::PlatformFileInfo file_info_; +}; + +// A wrapper file enumerator which returns a virtual path of the path returned +// by the wrapped enumerator. +// +// A virtual path is constructed as following: +// virtual_path = |virtual_base_path| + relative-path-to-|platform_base_path| +// +// Where |virtual_base_path| is in our case the virtual top-level directory +// that looks like: '/<filesystem_id>'. +// +// Example: +// Suppose virtual_base_path is: '/CAFEBABE', +// platform_base_path is: '/full/path/to/example/dir', and +// a path returned by wrapped_is: '/full/path/to/example/dir/a/b/c', +// Next() would return: '/CAFEBABE/dir/a/b/c'. +// +class PathConverterEnumerator + : public FileSystemFileUtil::AbstractFileEnumerator { + public: + PathConverterEnumerator( + FileSystemFileUtil::AbstractFileEnumerator* wrapped, + const FilePath& virtual_base_path, + const FilePath& platform_base_path) + : wrapped_(wrapped), + virtual_base_path_(virtual_base_path), + platform_base_path_(platform_base_path) {} + virtual ~PathConverterEnumerator() {} + + // AbstractFileEnumerator overrides. + virtual FilePath Next() OVERRIDE { + DCHECK(wrapped_.get()); + FilePath path = wrapped_->Next(); + if (path.empty()) + return path; + FilePath virtual_path = virtual_base_path_; + platform_base_path_.DirName().AppendRelativePath(path, &virtual_path); + return virtual_path; + } + virtual int64 Size() OVERRIDE { return wrapped_->Size(); } + virtual bool IsDirectory() OVERRIDE { return wrapped_->IsDirectory(); } + virtual base::Time LastModifiedTime() OVERRIDE { + return wrapped_->LastModifiedTime(); + } + virtual bool IsLink() OVERRIDE { return wrapped_->IsLink(); } + + private: + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> wrapped_; + FilePath virtual_base_path_; + FilePath platform_base_path_; +}; + +// Recursively enumerate each path from a given paths set. +class RecursiveSetFileEnumerator + : public FileSystemFileUtil::AbstractFileEnumerator { + public: + RecursiveSetFileEnumerator(FileSystemFileUtil* underlying_file_util, + const FileSystemOperationContext& context, + const FilePath& virtual_base_path, + const std::vector<FilePath>& paths, + const FileSystemPath& root) + : underlying_file_util_(underlying_file_util), + context_(context), + virtual_base_path_(virtual_base_path), + paths_(paths), + root_(root) { + DCHECK(underlying_file_util_); + path_iter_ = paths_.begin(); + current_enumerator_.reset( + new SetFileEnumerator(underlying_file_util, context, paths, root)); + } + virtual ~RecursiveSetFileEnumerator() {} + + // AbstractFileEnumerator overrides. + virtual FilePath Next() OVERRIDE; + virtual int64 Size() OVERRIDE { + DCHECK(current_enumerator_.get()); + return current_enumerator_->Size(); + } + virtual bool IsDirectory() OVERRIDE { + DCHECK(current_enumerator_.get()); + return current_enumerator_->IsDirectory(); + } + virtual base::Time LastModifiedTime() OVERRIDE { + DCHECK(current_enumerator_.get()); + return current_enumerator_->LastModifiedTime(); + } + virtual bool IsLink() OVERRIDE { + DCHECK(current_enumerator_.get()); + return current_enumerator_->IsLink(); + } + + private: + // Not owned. + FileSystemFileUtil* underlying_file_util_; + + FileSystemOperationContext context_; + FilePath virtual_base_path_; + std::vector<FilePath> paths_; + std::vector<FilePath>::iterator path_iter_; + base::PlatformFileInfo file_info_; + FileSystemPath root_; + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> current_enumerator_; +}; + +FilePath RecursiveSetFileEnumerator::Next() { + if (current_enumerator_.get()) { + FilePath path = current_enumerator_->Next(); + if (!path.empty()) + return path; + } + + // We reached the end. + if (path_iter_ == paths_.end()) + return FilePath(); + + // Enumerates subdirectories of the next path. + FilePath next_path = *path_iter_++; + current_enumerator_.reset( + new PathConverterEnumerator( + underlying_file_util_->CreateFileEnumerator( + &context_, root_.WithInternalPath(next_path), + true /* recursive */), + virtual_base_path_, next_path)); + DCHECK(current_enumerator_.get()); + return current_enumerator_->Next(); +} + +} // namespace + +IsolatedFileUtil::IsolatedFileUtil(FileSystemFileUtil* underlying_file_util) + : FileSystemFileUtil(underlying_file_util) { + DCHECK(underlying_file_util); +} + +PlatformFileError IsolatedFileUtil::CreateOrOpen( + FileSystemOperationContext* context, + const FileSystemPath& path, int file_flags, + PlatformFile* file_handle, bool* created) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::Close( + FileSystemOperationContext* context, + PlatformFile file_handle) { + // We don't allow open thus Close won't be called. + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::EnsureFileExists( + FileSystemOperationContext* context, + const FileSystemPath& path, + bool* created) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::CreateDirectory( + FileSystemOperationContext* context, + const FileSystemPath& path, + bool exclusive, + bool recursive) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::GetFileInfo( + FileSystemOperationContext* context, + const FileSystemPath& path, + PlatformFileInfo* file_info, + FilePath* platform_path) { + DCHECK(file_info); + std::string filesystem_id; + FilePath root_unused, cracked_path; + if (!IsolatedContext::GetInstance()->CrackIsolatedPath( + path.internal_path(), &filesystem_id, &root_unused, &cracked_path)) + return base::PLATFORM_FILE_ERROR_SECURITY; + if (cracked_path.empty()) { + // The root directory case. + // For now we leave three time fields (modified/accessed/creation time) + // NULL as it is not really clear what to be set for this virtual directory. + // TODO(kinuko): Maybe we want to set the time when this filesystem is + // created (i.e. when the files/directories are dropped). + file_info->is_directory = true; + file_info->is_symbolic_link = false; + file_info->size = 0; + return base::PLATFORM_FILE_OK; + } + return underlying_file_util()->GetFileInfo( + context, path.WithInternalPath(cracked_path), file_info, platform_path); +} + +FileSystemFileUtil::AbstractFileEnumerator* +IsolatedFileUtil::CreateFileEnumerator( + FileSystemOperationContext* context, + const FileSystemPath& root, + bool recursive) { + std::string filesystem_id; + FilePath root_path, cracked_path; + if (!IsolatedContext::GetInstance()->CrackIsolatedPath( + root.internal_path(), &filesystem_id, &root_path, &cracked_path)) + return NULL; + + FilePath virtual_base_path = + IsolatedContext::GetInstance()->CreateVirtualPath(filesystem_id, + FilePath()); + + if (!cracked_path.empty()) { + return new PathConverterEnumerator( + underlying_file_util()->CreateFileEnumerator( + context, root.WithInternalPath(cracked_path), recursive), + virtual_base_path, root_path); + } + + // Root path case. + std::vector<FilePath> toplevels; + IsolatedContext::GetInstance()->GetTopLevelPaths(filesystem_id, &toplevels); + if (!recursive) + return new SetFileEnumerator(underlying_file_util(), *context, + toplevels, root); + return new RecursiveSetFileEnumerator(underlying_file_util(), *context, + virtual_base_path, toplevels, root); +} + +PlatformFileError IsolatedFileUtil::Touch( + FileSystemOperationContext* context, + const FileSystemPath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::Truncate( + FileSystemOperationContext* context, + const FileSystemPath& path, + int64 length) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +bool IsolatedFileUtil::PathExists( + FileSystemOperationContext* context, + const FileSystemPath& path) { + FilePath platform_path; + if (!GetPlatformPath(path, &platform_path)) + return false; + if (platform_path.empty()) { + // The root directory case. + return true; + } + return underlying_file_util()->PathExists( + context, path.WithInternalPath(platform_path)); +} + +bool IsolatedFileUtil::DirectoryExists( + FileSystemOperationContext* context, + const FileSystemPath& path) { + FilePath platform_path; + if (!GetPlatformPath(path, &platform_path)) + return false; + if (platform_path.empty()) { + // The root directory case. + return true; + } + return underlying_file_util()->DirectoryExists( + context, path.WithInternalPath(platform_path)); +} + +bool IsolatedFileUtil::IsDirectoryEmpty( + FileSystemOperationContext* context, + const FileSystemPath& path) { + std::string filesystem_id; + FilePath platform_path; + if (!GetPlatformPath(path, &platform_path)) + return false; + if (platform_path.empty()) { + // The root directory case. + std::vector<FilePath> toplevels; + bool success = IsolatedContext::GetInstance()->GetTopLevelPaths( + filesystem_id, &toplevels); + DCHECK(success); + return toplevels.empty(); + } + return underlying_file_util()->IsDirectoryEmpty( + context, path.WithInternalPath(platform_path)); +} + +PlatformFileError IsolatedFileUtil::CopyOrMoveFile( + FileSystemOperationContext* context, + const FileSystemPath& src_path, + const FileSystemPath& dest_path, + bool copy) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::CopyInForeignFile( + FileSystemOperationContext* context, + const FileSystemPath& src_path, + const FileSystemPath& dest_path) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::DeleteFile( + FileSystemOperationContext* context, + const FileSystemPath& path) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +PlatformFileError IsolatedFileUtil::DeleteSingleDirectory( + FileSystemOperationContext* context, + const FileSystemPath& path) { + return base::PLATFORM_FILE_ERROR_SECURITY; +} + +bool IsolatedFileUtil::GetPlatformPath(const FileSystemPath& virtual_path, + FilePath* platform_path) const { + DCHECK(platform_path); + std::string filesystem_id; + FilePath root_path; + if (!IsolatedContext::GetInstance()->CrackIsolatedPath( + virtual_path.internal_path(), &filesystem_id, + &root_path, platform_path)) + return false; + return true; +} + +} // namespace diff --git a/webkit/fileapi/isolated_file_util.h b/webkit/fileapi/isolated_file_util.h new file mode 100644 index 0000000..d49c6ac --- /dev/null +++ b/webkit/fileapi/isolated_file_util.h @@ -0,0 +1,98 @@ +// Copyright (c) 2012 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_ISOLATED_FILE_UTIL_H_ +#define WEBKIT_FILEAPI_ISOLATED_FILE_UTIL_H_ + +#include "base/file_path.h" +#include "base/file_util_proxy.h" +#include "base/platform_file.h" +#include "webkit/fileapi/file_system_file_util.h" + +namespace base { +class Time; +} + +namespace fileapi { + +class FileSystemOperationContext; +class IsolatedContext; + +class IsolatedFileUtil : public FileSystemFileUtil { + public: + IsolatedFileUtil(FileSystemFileUtil* underlying_file_util); + virtual ~IsolatedFileUtil() {} + + // FileSystemFileUtil overrides. + virtual base::PlatformFileError CreateOrOpen( + FileSystemOperationContext* context, + const FileSystemPath& path, + int file_flags, + base::PlatformFile* file_handle, + bool* created) OVERRIDE; + virtual base::PlatformFileError Close( + FileSystemOperationContext* context, + base::PlatformFile) OVERRIDE; + virtual base::PlatformFileError EnsureFileExists( + FileSystemOperationContext* context, + const FileSystemPath& path, bool* created) OVERRIDE; + virtual base::PlatformFileError CreateDirectory( + FileSystemOperationContext* context, + const FileSystemPath& path, + bool exclusive, + bool recursive) OVERRIDE; + virtual base::PlatformFileError GetFileInfo( + FileSystemOperationContext* context, + const FileSystemPath& path, + base::PlatformFileInfo* file_info, + FilePath* platform_path) OVERRIDE; + virtual AbstractFileEnumerator* CreateFileEnumerator( + FileSystemOperationContext* context, + const FileSystemPath& root_path, + bool recursive) OVERRIDE; + virtual base::PlatformFileError Touch( + FileSystemOperationContext* context, + const FileSystemPath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time) OVERRIDE; + virtual base::PlatformFileError Truncate( + FileSystemOperationContext* context, + const FileSystemPath& path, + int64 length) OVERRIDE; + virtual bool PathExists( + FileSystemOperationContext* context, + const FileSystemPath& path) OVERRIDE; + virtual bool DirectoryExists( + FileSystemOperationContext* context, + const FileSystemPath& path) OVERRIDE; + virtual bool IsDirectoryEmpty( + FileSystemOperationContext* context, + const FileSystemPath& path) OVERRIDE; + virtual base::PlatformFileError CopyOrMoveFile( + FileSystemOperationContext* context, + const FileSystemPath& src_path, + const FileSystemPath& dest_path, + bool copy) OVERRIDE; + virtual base::PlatformFileError CopyInForeignFile( + FileSystemOperationContext* context, + const FileSystemPath& underlying_src_path, + const FileSystemPath& dest_path) OVERRIDE; + virtual base::PlatformFileError DeleteFile( + FileSystemOperationContext* context, + const FileSystemPath& path) OVERRIDE; + virtual base::PlatformFileError DeleteSingleDirectory( + FileSystemOperationContext* context, + const FileSystemPath& path) OVERRIDE; + + private: + // Returns false if the given |virtual_path| is not a valid path. + bool GetPlatformPath(const FileSystemPath& virtual_path, + FilePath* platform_path) const; + + DISALLOW_COPY_AND_ASSIGN(IsolatedFileUtil); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_ISOLATED_FILE_UTIL_H_ diff --git a/webkit/fileapi/isolated_file_util_unittest.cc b/webkit/fileapi/isolated_file_util_unittest.cc new file mode 100644 index 0000000..a422ff9 --- /dev/null +++ b/webkit/fileapi/isolated_file_util_unittest.cc @@ -0,0 +1,432 @@ +// Copyright (c) 2012 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 <string> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop_proxy.h" +#include "base/scoped_temp_dir.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/file_system_test_helper.h" +#include "webkit/fileapi/file_util_helper.h" +#include "webkit/fileapi/isolated_context.h" +#include "webkit/fileapi/isolated_file_util.h" +#include "webkit/fileapi/local_file_util.h" +#include "webkit/fileapi/mock_file_system_options.h" +#include "webkit/fileapi/native_file_util.h" +#include "webkit/fileapi/test_file_set.h" +#include "webkit/quota/mock_special_storage_policy.h" + +using file_util::FileEnumerator; + +namespace fileapi { + +namespace { + +// Used in IsolatedFileUtilTest::SimulateDropFiles(). +// Random root paths in which we create each file/directory of the +// RegularTestCases (so that we can simulate a drop with files/directories +// from multiple directories). +static const FilePath::CharType* kRootPaths[] = { + FILE_PATH_LITERAL("a"), + FILE_PATH_LITERAL("b/c"), + FILE_PATH_LITERAL("etc"), +}; + +FilePath GetTopLevelPath(const FilePath& path) { + std::vector<FilePath::StringType> components; + path.GetComponents(&components); + return FilePath(components[0]); +} + +} // namespace + +class IsolatedFileUtilTest : public testing::Test { + public: + IsolatedFileUtilTest() {} + + void SetUp() { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + file_util_.reset(new IsolatedFileUtil(new NativeFileUtil())); + + // Register the files/directories of RegularTestCases (with random + // root paths) as dropped files. + SimulateDropFiles(); + + file_system_context_ = new FileSystemContext( + base::MessageLoopProxy::current(), + base::MessageLoopProxy::current(), + new quota::MockSpecialStoragePolicy(), + NULL /* quota_manager */, + data_dir_.path(), + CreateAllowFileAccessOptions()); + + // For cross-FileUtil copy/move tests. + other_file_util_.reset(new LocalFileUtil(new NativeFileUtil())); + other_file_util_helper_.SetUp(file_system_context_, other_file_util_.get()); + } + + void TearDown() { + isolated_context()->RevokeIsolatedFileSystem(filesystem_id_); + other_file_util_helper_.TearDown(); + } + + protected: + IsolatedContext* isolated_context() const { + return IsolatedContext::GetInstance(); + } + const FilePath& root_path() const { + return data_dir_.path(); + } + FileSystemContext* file_system_context() const { + return file_system_context_.get(); + } + FileSystemFileUtil* file_util() const { return file_util_.get(); } + FileSystemFileUtil* other_file_util() const { return other_file_util_.get(); } + std::string filesystem_id() const { return filesystem_id_; } + + FilePath GetTestCasePlatformPath(const FilePath::StringType& path) { + return toplevel_root_map_[GetTopLevelPath(FilePath(path))].Append(path). + NormalizePathSeparators(); + } + + FileSystemPath GetFileSystemPath(const FilePath& path) const { + FilePath virtual_path = isolated_context()->CreateVirtualPath( + filesystem_id(), path); + return FileSystemPath(GURL("http://example.com"), + kFileSystemTypeIsolated, + virtual_path); + } + + FileSystemPath GetOtherFileSystemPath(const FilePath& path) { + return other_file_util_helper_.CreatePath(path); + } + + void VerifyFilesHaveSameContent(FileSystemFileUtil* file_util1, + FileSystemFileUtil* file_util2, + const FileSystemPath& path1, + const FileSystemPath& path2) { + scoped_ptr<FileSystemOperationContext> context; + + // Get the file info for path1. + base::PlatformFileInfo info1; + FilePath platform_path1; + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util1->GetFileInfo(context.get(), path1, + &info1, &platform_path1)); + + // Get the file info for path2. + base::PlatformFileInfo info2; + FilePath platform_path2; + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util2->GetFileInfo(context.get(), path2, + &info2, &platform_path2)); + + // See if file info matches with the other one. + EXPECT_EQ(info1.is_directory, info2.is_directory); + EXPECT_EQ(info1.size, info2.size); + EXPECT_EQ(info1.is_symbolic_link, info2.is_symbolic_link); + EXPECT_NE(platform_path1, platform_path2); + + std::string content1, content2; + EXPECT_TRUE(file_util::ReadFileToString(platform_path1, &content1)); + EXPECT_TRUE(file_util::ReadFileToString(platform_path2, &content2)); + EXPECT_EQ(content1, content2); + } + + void VerifyDirectoriesHaveSameContent(FileSystemFileUtil* file_util1, + FileSystemFileUtil* file_util2, + const FileSystemPath& root1, + const FileSystemPath& root2) { + scoped_ptr<FileSystemOperationContext> context; + + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum1; + context.reset(new FileSystemOperationContext(file_system_context())); + file_enum1.reset(file_util1->CreateFileEnumerator( + context.get(), root1, true /* recursive */)); + + FilePath current; + std::set<FilePath> file_set1; + while (!(current = file_enum1->Next()).empty()) { + if (file_enum1->IsDirectory()) + continue; + file_set1.insert(current); + } + + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum2; + context.reset(new FileSystemOperationContext(file_system_context())); + file_enum2.reset(file_util2->CreateFileEnumerator( + context.get(), root2, true /* recursive */)); + + while (!(current = file_enum2->Next()).empty()) { + FileSystemPath path1 = root1.WithInternalPath(current); + FileSystemPath path2 = root2.WithInternalPath(current); + if (file_enum2->IsDirectory()) { + FileSystemOperationContext context1(file_system_context()); + FileSystemOperationContext context2(file_system_context()); + EXPECT_EQ(file_util1->IsDirectoryEmpty(&context1, path1), + file_util2->IsDirectoryEmpty(&context2, path2)); + continue; + } + EXPECT_TRUE(file_set1.find(current) != file_set1.end()); + VerifyFilesHaveSameContent(file_util1, file_util2, path1, path2); + } + } + + private: + void SimulateDropFiles() { + size_t root_path_index = 0; + + std::set<FilePath> toplevels; + for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { + const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; + FilePath path(test_case.path); + FilePath toplevel = GetTopLevelPath(path); + + // We create the test case files under one of the kRootPaths + // to simulate a drop with multiple directories. + if (toplevel_root_map_.find(toplevel) == toplevel_root_map_.end()) { + FilePath root = root_path().Append( + kRootPaths[(root_path_index++) % arraysize(kRootPaths)]); + toplevel_root_map_[toplevel] = root; + toplevels.insert(root.Append(path)); + } + + test::SetUpOneTestCase(toplevel_root_map_[toplevel], test_case); + } + + // Register the toplevel entries. + filesystem_id_ = isolated_context()->RegisterIsolatedFileSystem(toplevels); + } + + ScopedTempDir data_dir_; + std::string filesystem_id_; + scoped_refptr<FileSystemContext> file_system_context_; + std::map<FilePath, FilePath> toplevel_root_map_; + scoped_ptr<IsolatedFileUtil> file_util_; + scoped_ptr<LocalFileUtil> other_file_util_; + FileSystemTestOriginHelper other_file_util_helper_; + DISALLOW_COPY_AND_ASSIGN(IsolatedFileUtilTest); +}; + +TEST_F(IsolatedFileUtilTest, BasicTest) { + for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { + SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i); + const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; + + FileSystemPath path = GetFileSystemPath(FilePath(test_case.path)); + + // See if we can query the file info via the isolated FileUtil. + // (This should succeed since we have registered all the top-level + // entries of the test cases in SetUp()) + base::PlatformFileInfo info; + FilePath platform_path; + FileSystemOperationContext context(file_system_context()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util()->GetFileInfo(&context, path, &info, &platform_path)); + + // See if the obtained file info is correct. + if (!test_case.is_directory) + ASSERT_EQ(test_case.data_file_size, info.size); + ASSERT_EQ(test_case.is_directory, info.is_directory); + ASSERT_EQ(GetTestCasePlatformPath(test_case.path), + platform_path.NormalizePathSeparators()); + } +} + +TEST_F(IsolatedFileUtilTest, UnregisteredPathsTest) { + static const fileapi::test::TestCaseRecord kUnregisteredCases[] = { + {true, FILE_PATH_LITERAL("nonexistent"), 0}, + {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0}, + {false, FILE_PATH_LITERAL("nonexistent/false"), 0}, + {false, FILE_PATH_LITERAL("foo"), 30}, + {false, FILE_PATH_LITERAL("bar"), 20}, + }; + + for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) { + SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i); + const test::TestCaseRecord& test_case = kUnregisteredCases[i]; + + // Prepare the test file/directory. + SetUpOneTestCase(root_path(), test_case); + + // Make sure regular GetFileInfo succeeds. + base::PlatformFileInfo info; + ASSERT_TRUE(file_util::GetFileInfo( + root_path().Append(test_case.path), &info)); + if (!test_case.is_directory) + ASSERT_EQ(test_case.data_file_size, info.size); + ASSERT_EQ(test_case.is_directory, info.is_directory); + } + + for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) { + SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i); + const test::TestCaseRecord& test_case = kUnregisteredCases[i]; + FileSystemPath path = GetFileSystemPath(FilePath(test_case.path)); + + // This should fail as the paths in kUnregisteredCases are not included + // in the dropped files (i.e. the regular test cases). + base::PlatformFileInfo info; + FilePath platform_path; + FileSystemOperationContext context(file_system_context()); + ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, + file_util()->GetFileInfo(&context, path, &info, &platform_path)); + } +} + +TEST_F(IsolatedFileUtilTest, ReadDirectoryTest) { + for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { + const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; + if (!test_case.is_directory) + continue; + + SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i + << ": " << test_case.path); + + // Read entries in the directory to construct the expected results map. + typedef std::map<FilePath::StringType, base::FileUtilProxy::Entry> EntryMap; + EntryMap expected_entry_map; + + FileEnumerator file_enum( + GetTestCasePlatformPath(test_case.path), + false /* recursive */, + static_cast<file_util::FileEnumerator::FileType>( + FileEnumerator::FILES | FileEnumerator::DIRECTORIES)); + FilePath current; + while (!(current = file_enum.Next()).empty()) { + FileEnumerator::FindInfo file_info; + file_enum.GetFindInfo(&file_info); + base::FileUtilProxy::Entry entry; + entry.is_directory = FileEnumerator::IsDirectory(file_info); + entry.name = current.BaseName().value(); + entry.size = FileEnumerator::GetFilesize(file_info); + entry.last_modified_time = FileEnumerator::GetLastModifiedTime(file_info); + expected_entry_map[entry.name] = entry; + } + + // Perform ReadDirectory in the isolated filesystem. + FileSystemPath path = GetFileSystemPath(FilePath(test_case.path)); + std::vector<base::FileUtilProxy::Entry> entries; + FileSystemOperationContext context(file_system_context()); + ASSERT_EQ(base::PLATFORM_FILE_OK, + FileUtilHelper::ReadDirectory( + &context, file_util(), path, &entries)); + + EXPECT_EQ(expected_entry_map.size(), entries.size()); + for (size_t i = 0; i < entries.size(); ++i) { + const base::FileUtilProxy::Entry& entry = entries[i]; + EntryMap::iterator found = expected_entry_map.find(entry.name); + EXPECT_TRUE(found != expected_entry_map.end()); + EXPECT_EQ(found->second.name, entry.name); + EXPECT_EQ(found->second.is_directory, entry.is_directory); + EXPECT_EQ(found->second.size, entry.size); + EXPECT_EQ(found->second.last_modified_time.ToDoubleT(), + entry.last_modified_time.ToDoubleT()); + } + } +} + +TEST_F(IsolatedFileUtilTest, CopyOutFileTest) { + scoped_ptr<FileSystemOperationContext> context( + new FileSystemOperationContext(file_system_context())); + FileSystemPath root_path = GetFileSystemPath(FilePath()); + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( + file_util()->CreateFileEnumerator(context.get(), + root_path, + true /* recursive */)); + FilePath current; + while (!(current = file_enum->Next()).empty()) { + if (file_enum->IsDirectory()) + continue; + + SCOPED_TRACE(testing::Message() << "Testing file copy " + << current.value()); + + FileSystemPath src_path = root_path.WithInternalPath(current); + FileSystemPath dest_path = GetOtherFileSystemPath(current); + + // Create the parent directory of the destination. + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + other_file_util()->CreateDirectory( + context.get(), + GetOtherFileSystemPath(current.DirName()), + false /* exclusive */, + true /* recursive */)); + + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + FileUtilHelper::Copy( + context.get(), + file_util(), other_file_util(), + src_path, dest_path)); + + // The other way (copy-in) should not work. + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, + FileUtilHelper::Copy( + context.get(), + other_file_util(), file_util(), + dest_path, src_path)); + + VerifyFilesHaveSameContent(file_util(), other_file_util(), + src_path, dest_path); + } +} + +TEST_F(IsolatedFileUtilTest, CopyOutDirectoryTest) { + scoped_ptr<FileSystemOperationContext> context( + new FileSystemOperationContext(file_system_context())); + FileSystemPath root_path = GetFileSystemPath(FilePath()); + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( + file_util()->CreateFileEnumerator(context.get(), + root_path, + false /* recursive */)); + FilePath current; + while (!(current = file_enum->Next()).empty()) { + if (!file_enum->IsDirectory()) + continue; + + SCOPED_TRACE(testing::Message() << "Testing directory copy " + << current.value()); + + FileSystemPath src_path = root_path.WithInternalPath(current); + FileSystemPath dest_path = GetOtherFileSystemPath(current); + + // Create the parent directory of the destination. + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + other_file_util()->CreateDirectory( + context.get(), + GetOtherFileSystemPath(current.DirName()), + false /* exclusive */, + true /* recursive */)); + + context.reset(new FileSystemOperationContext(file_system_context())); + ASSERT_EQ(base::PLATFORM_FILE_OK, + FileUtilHelper::Copy( + context.get(), + file_util(), other_file_util(), + src_path, dest_path)); + + // The other way (copy-in) should not work for two reasons: + // write is prohibited in the isolated filesystem, and copying directory + // to non-empty directory shouldn't work. + context.reset(new FileSystemOperationContext(file_system_context())); + base::PlatformFileError result = FileUtilHelper::Copy( + context.get(), other_file_util(), file_util(), dest_path, src_path); + ASSERT_TRUE(result == base::PLATFORM_FILE_ERROR_FAILED || + result == base::PLATFORM_FILE_ERROR_NOT_EMPTY); + + VerifyDirectoriesHaveSameContent(file_util(), other_file_util(), + src_path, dest_path); + } +} + +} // namespace fileapi diff --git a/webkit/fileapi/isolated_mount_point_provider.cc b/webkit/fileapi/isolated_mount_point_provider.cc new file mode 100644 index 0000000..e05833c --- /dev/null +++ b/webkit/fileapi/isolated_mount_point_provider.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2012 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/isolated_mount_point_provider.h" + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/message_loop_proxy.h" +#include "googleurl/src/gurl.h" +#include "webkit/fileapi/file_system_callback_dispatcher.h" +#include "webkit/fileapi/file_system_operation.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/isolated_context.h" +#include "webkit/fileapi/isolated_file_util.h" +#include "webkit/fileapi/native_file_util.h" + +namespace fileapi { + +IsolatedMountPointProvider::IsolatedMountPointProvider() + : isolated_file_util_(new IsolatedFileUtil(new NativeFileUtil())) { +} + +IsolatedMountPointProvider::~IsolatedMountPointProvider() { +} + +void IsolatedMountPointProvider::ValidateFileSystemRoot( + const GURL& origin_url, + FileSystemType type, + bool create, + const ValidateFileSystemCallback& callback) { + // We never allow opening a new isolated FileSystem via usual OpenFileSystem. + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(callback, base::PLATFORM_FILE_ERROR_SECURITY)); +} + +FilePath IsolatedMountPointProvider::GetFileSystemRootPathOnFileThread( + const GURL& origin_url, + FileSystemType type, + const FilePath& virtual_path, + bool create) { + if (create || type != kFileSystemTypeIsolated) + return FilePath(); + std::string fsid; + FilePath root, path; + if (!isolated_context()->CrackIsolatedPath(virtual_path, &fsid, &root, &path)) + return FilePath(); + return root; +} + +bool IsolatedMountPointProvider::IsAccessAllowed( + const GURL& origin_url, FileSystemType type, const FilePath& virtual_path) { + if (type != fileapi::kFileSystemTypeIsolated) + return false; + + std::string filesystem_id; + FilePath root, path; + return isolated_context()->CrackIsolatedPath( + virtual_path, &filesystem_id, &root, &path); +} + +bool IsolatedMountPointProvider::IsRestrictedFileName( + const FilePath& filename) const { + return false; +} + +std::vector<FilePath> IsolatedMountPointProvider::GetRootDirectories() const { + // We have no pre-defined root directories that need to be given + // access permission. + return std::vector<FilePath>(); +} + +FileSystemFileUtil* IsolatedMountPointProvider::GetFileUtil() { + return isolated_file_util_.get(); +} + +FilePath IsolatedMountPointProvider::GetPathForPermissionsCheck( + const FilePath& virtual_path) const { + std::string fsid; + FilePath root, path; + if (!isolated_context()->CrackIsolatedPath(virtual_path, &fsid, &root, &path)) + return FilePath(); + return path; +} + +FileSystemOperationInterface* +IsolatedMountPointProvider::CreateFileSystemOperation( + const GURL& origin_url, + FileSystemType file_system_type, + const FilePath& virtual_path, + base::MessageLoopProxy* file_proxy, + FileSystemContext* context) const { + return new FileSystemOperation(file_proxy, context); +} + +IsolatedContext* IsolatedMountPointProvider::isolated_context() const { + return IsolatedContext::GetInstance(); +} + +} // namespace fileapi diff --git a/webkit/fileapi/isolated_mount_point_provider.h b/webkit/fileapi/isolated_mount_point_provider.h new file mode 100644 index 0000000..3784b40 --- /dev/null +++ b/webkit/fileapi/isolated_mount_point_provider.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012 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_ISOLATED_MOUNT_POINT_PROVIDER_H_ +#define WEBKIT_FILEAPI_ISOLATED_MOUNT_POINT_PROVIDER_H_ + +#include "base/memory/scoped_ptr.h" +#include "webkit/fileapi/file_system_mount_point_provider.h" + +namespace fileapi { + +class IsolatedContext; + +class IsolatedMountPointProvider : public FileSystemMountPointProvider { + public: + typedef FileSystemMountPointProvider::ValidateFileSystemCallback + ValidateFileSystemCallback; + + IsolatedMountPointProvider(); + virtual ~IsolatedMountPointProvider(); + + // FileSystemMountPointProvider implementation. + virtual void ValidateFileSystemRoot( + const GURL& origin_url, + FileSystemType type, + bool create, + const ValidateFileSystemCallback& callback) OVERRIDE; + virtual FilePath GetFileSystemRootPathOnFileThread( + const GURL& origin_url, + FileSystemType type, + const FilePath& virtual_path, + bool create) OVERRIDE; + virtual bool IsAccessAllowed(const GURL& origin_url, + FileSystemType type, + const FilePath& virtual_path) OVERRIDE; + virtual bool IsRestrictedFileName(const FilePath& filename) const OVERRIDE; + virtual std::vector<FilePath> GetRootDirectories() const OVERRIDE; + virtual FileSystemFileUtil* GetFileUtil() OVERRIDE; + virtual FilePath GetPathForPermissionsCheck(const FilePath& virtual_path) + const OVERRIDE; + virtual FileSystemOperationInterface* CreateFileSystemOperation( + const GURL& origin_url, + FileSystemType file_system_type, + const FilePath& virtual_path, + base::MessageLoopProxy* file_proxy, + FileSystemContext* context) const OVERRIDE; + + private: + IsolatedContext* isolated_context() const; + + scoped_ptr<FileSystemFileUtil> isolated_file_util_; +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_ISOLATED_MOUNT_POINT_PROVIDER_H_ diff --git a/webkit/fileapi/test_file_set.cc b/webkit/fileapi/test_file_set.cc index badb7fb..995ab6f 100644 --- a/webkit/fileapi/test_file_set.cc +++ b/webkit/fileapi/test_file_set.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/platform_file.h" +#include "base/rand_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace fileapi { @@ -15,7 +16,7 @@ namespace test { const TestCaseRecord kRegularTestCases[] = { {true, FILE_PATH_LITERAL("dir a"), 0}, - {true, FILE_PATH_LITERAL("dir a/dir a"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir A"), 0}, {true, FILE_PATH_LITERAL("dir a/dir d"), 0}, {true, FILE_PATH_LITERAL("dir a/dir d/dir e"), 0}, {true, FILE_PATH_LITERAL("dir a/dir d/dir e/dir f"), 0}, @@ -41,18 +42,22 @@ void SetUpOneTestCase(const FilePath& root_path, FilePath path = root_path.Append(test_case.path); if (test_case.is_directory) { ASSERT_TRUE(file_util::CreateDirectory(path)); - } else { - base::PlatformFileError error_code; - bool created = false; - int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; - base::PlatformFile file_handle = - base::CreatePlatformFile(path, file_flags, &created, &error_code); - EXPECT_TRUE(created); - ASSERT_NE(base::kInvalidPlatformFileValue, file_handle); - ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); - ASSERT_TRUE( - base::TruncatePlatformFile(file_handle, test_case.data_file_size)); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); + return; + } + base::PlatformFileError error_code; + bool created = false; + int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE; + base::PlatformFile file_handle = + base::CreatePlatformFile(path, file_flags, &created, &error_code); + EXPECT_TRUE(created); + ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); + ASSERT_NE(base::kInvalidPlatformFileValue, file_handle); + EXPECT_TRUE(base::ClosePlatformFile(file_handle)); + if (test_case.data_file_size > 0U) { + std::string content = base::RandBytesAsString(test_case.data_file_size); + ASSERT_EQ(static_cast<int>(content.size()), + file_util::WriteFile(path, content.data(), content.size())); } } diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index 4ac71cf..195f187 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -58,6 +58,10 @@ 'file_writer_delegate.h', 'isolated_context.cc', 'isolated_context.h', + 'isolated_file_util.cc', + 'isolated_file_util.h', + 'isolated_mount_point_provider.cc', + 'isolated_mount_point_provider.h', 'local_file_util.cc', 'local_file_util.h', 'native_file_util.cc', diff --git a/webkit/glue/webdropdata.cc b/webkit/glue/webdropdata.cc index 43323f8..02a0a94 100644 --- a/webkit/glue/webdropdata.cc +++ b/webkit/glue/webdropdata.cc @@ -124,9 +124,6 @@ WebDragData WebDropData::ToDragData() const { WebDragData result; result.initialize(); result.setItems(item_list); -#ifdef WEBKIT_DRAG_DROP_SUPPORT_FILESYSTEM - // TODO(kinuko): remove this ifdef once we update the WebKit API. result.setFilesystemId(filesystem_id); -#endif return result; } diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 5358649..354ac6e 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -419,6 +419,7 @@ '../../fileapi/file_system_usage_cache_unittest.cc', '../../fileapi/file_system_util_unittest.cc', '../../fileapi/isolated_context_unittest.cc', + '../../fileapi/isolated_file_util_unittest.cc', '../../fileapi/local_file_util_unittest.cc', '../../fileapi/mock_file_system_options.cc', '../../fileapi/mock_file_system_options.h', |