// Copyright 2014 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/basictypes.h" #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" #define FPL(x) FILE_PATH_LITERAL(x) #if defined(FILE_PATH_USES_DRIVE_LETTERS) #define DRIVE FPL("C:") #else #define DRIVE #endif using fileapi::FileSystemMountOption; using fileapi::FileSystemURL; using fileapi::IsolatedContext; using fileapi::kFileSystemTypeDragged; using fileapi::kFileSystemTypeIsolated; using fileapi::kFileSystemTypeNativeLocal; namespace content { typedef IsolatedContext::MountPointInfo FileInfo; namespace { const base::FilePath kTestPaths[] = { base::FilePath(DRIVE FPL("/a/b.txt")), base::FilePath(DRIVE FPL("/c/d/e")), base::FilePath(DRIVE FPL("/h/")), base::FilePath(DRIVE FPL("/")), #if defined(FILE_PATH_USES_WIN_SEPARATORS) base::FilePath(DRIVE FPL("\\foo\\bar")), base::FilePath(DRIVE FPL("\\")), #endif // For duplicated base name test. base::FilePath(DRIVE FPL("/")), base::FilePath(DRIVE FPL("/f/e")), base::FilePath(DRIVE FPL("/f/b.txt")), }; } // namespace class IsolatedContextTest : public testing::Test { public: IsolatedContextTest() { for (size_t i = 0; i < arraysize(kTestPaths); ++i) fileset_.insert(kTestPaths[i].NormalizePathSeparators()); } virtual void SetUp() { IsolatedContext::FileInfoSet files; for (size_t i = 0; i < arraysize(kTestPaths); ++i) { std::string name; ASSERT_TRUE( files.AddPath(kTestPaths[i].NormalizePathSeparators(), &name)); names_.push_back(name); } id_ = IsolatedContext::GetInstance()->RegisterDraggedFileSystem(files); IsolatedContext::GetInstance()->AddReference(id_); ASSERT_FALSE(id_.empty()); } virtual void TearDown() { IsolatedContext::GetInstance()->RemoveReference(id_); } IsolatedContext* isolated_context() const { return IsolatedContext::GetInstance(); } protected: std::string id_; std::multiset<base::FilePath> fileset_; std::vector<std::string> names_; private: DISALLOW_COPY_AND_ASSIGN(IsolatedContextTest); }; TEST_F(IsolatedContextTest, RegisterAndRevokeTest) { // See if the returned top-level entries match with what we registered. std::vector<FileInfo> toplevels; ASSERT_TRUE(isolated_context()->GetDraggedFileInfo(id_, &toplevels)); ASSERT_EQ(fileset_.size(), toplevels.size()); for (size_t i = 0; i < toplevels.size(); ++i) { ASSERT_TRUE(fileset_.find(toplevels[i].path) != fileset_.end()); } // See if the name of each registered kTestPaths (that is what we // register in SetUp() by RegisterDraggedFileSystem) is properly cracked as // a valid virtual path in the isolated filesystem. for (size_t i = 0; i < arraysize(kTestPaths); ++i) { base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_) .AppendASCII(names_[i]); std::string cracked_id; base::FilePath cracked_path; std::string cracked_inner_id; fileapi::FileSystemType cracked_type; FileSystemMountOption cracked_option; ASSERT_TRUE(isolated_context()->CrackVirtualPath( virtual_path, &cracked_id, &cracked_type, &cracked_inner_id, &cracked_path, &cracked_option)); ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(), cracked_path.value()); ASSERT_EQ(id_, cracked_id); ASSERT_EQ(kFileSystemTypeDragged, cracked_type); EXPECT_TRUE(cracked_inner_id.empty()); } // Make sure GetRegisteredPath returns false for id_ since it is // registered for dragged files. base::FilePath path; ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path)); // Deref the current one and registering a new one. isolated_context()->RemoveReference(id_); std::string id2 = isolated_context()->RegisterFileSystemForPath( kFileSystemTypeNativeLocal, std::string(), base::FilePath(DRIVE FPL("/foo")), NULL); // Make sure the GetDraggedFileInfo returns false for both ones. ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id2, &toplevels)); ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id_, &toplevels)); // Make sure the GetRegisteredPath returns true only for the new one. ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path)); ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path)); // Try registering three more file systems for the same path as id2. std::string id3 = isolated_context()->RegisterFileSystemForPath( kFileSystemTypeNativeLocal, std::string(), path, NULL); std::string id4 = isolated_context()->RegisterFileSystemForPath( kFileSystemTypeNativeLocal, std::string(), path, NULL); std::string id5 = isolated_context()->RegisterFileSystemForPath( kFileSystemTypeNativeLocal, std::string(), path, NULL); // Remove file system for id4. isolated_context()->AddReference(id4); isolated_context()->RemoveReference(id4); // Only id4 should become invalid now. ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path)); ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path)); ASSERT_TRUE(isolated_context()->GetRegisteredPath(id5, &path)); // Revoke file system id5, after adding multiple references. isolated_context()->AddReference(id5); isolated_context()->AddReference(id5); isolated_context()->AddReference(id5); isolated_context()->RevokeFileSystem(id5); // No matter how many references we add id5 must be invalid now. ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path)); ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path)); // Revoke the file systems by path. isolated_context()->RevokeFileSystemByPath(path); // Now all the file systems associated to the path must be invalid. ASSERT_FALSE(isolated_context()->GetRegisteredPath(id2, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id3, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path)); ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path)); } TEST_F(IsolatedContextTest, CrackWithRelativePaths) { const struct { base::FilePath::StringType path; bool valid; } relatives[] = { { FPL("foo"), true }, { FPL("foo/bar"), true }, { FPL(".."), false }, { FPL("foo/.."), false }, { FPL("foo/../bar"), false }, #if defined(FILE_PATH_USES_WIN_SEPARATORS) # define SHOULD_FAIL_WITH_WIN_SEPARATORS false #else # define SHOULD_FAIL_WITH_WIN_SEPARATORS true #endif { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS }, { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS }, }; for (size_t i = 0; i < arraysize(kTestPaths); ++i) { for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) { SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value() << " " << relatives[j].path); base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_).AppendASCII( names_[i]).Append(relatives[j].path); std::string cracked_id; base::FilePath cracked_path; fileapi::FileSystemType cracked_type; std::string cracked_inner_id; FileSystemMountOption cracked_option; if (!relatives[j].valid) { ASSERT_FALSE(isolated_context()->CrackVirtualPath( virtual_path, &cracked_id, &cracked_type, &cracked_inner_id, &cracked_path, &cracked_option)); continue; } ASSERT_TRUE(isolated_context()->CrackVirtualPath( virtual_path, &cracked_id, &cracked_type, &cracked_inner_id, &cracked_path, &cracked_option)); ASSERT_EQ(kTestPaths[i].Append(relatives[j].path) .NormalizePathSeparators().value(), cracked_path.value()); ASSERT_EQ(id_, cracked_id); ASSERT_EQ(kFileSystemTypeDragged, cracked_type); EXPECT_TRUE(cracked_inner_id.empty()); } } } TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) { const struct { base::FilePath::StringType path; bool valid; } relatives[] = { { FPL("foo"), true }, { FPL("foo/bar"), true }, { FPL(".."), false }, { FPL("foo/.."), false }, { FPL("foo/../bar"), false }, #if defined(FILE_PATH_USES_WIN_SEPARATORS) # define SHOULD_FAIL_WITH_WIN_SEPARATORS false #else # define SHOULD_FAIL_WITH_WIN_SEPARATORS true #endif { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS }, { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS }, }; for (size_t i = 0; i < arraysize(kTestPaths); ++i) { for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) { SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value() << " " << relatives[j].path); base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_).AppendASCII( names_[i]).Append(relatives[j].path); FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL( GURL("http://chromium.org"), kFileSystemTypeIsolated, virtual_path); ASSERT_EQ(relatives[j].valid, cracked.is_valid()); if (!relatives[j].valid) continue; ASSERT_EQ(GURL("http://chromium.org"), cracked.origin()); ASSERT_EQ(kTestPaths[i].Append(relatives[j].path) .NormalizePathSeparators().value(), cracked.path().value()); ASSERT_EQ(virtual_path.NormalizePathSeparators(), cracked.virtual_path()); ASSERT_EQ(id_, cracked.filesystem_id()); ASSERT_EQ(kFileSystemTypeDragged, cracked.type()); ASSERT_EQ(kFileSystemTypeIsolated, cracked.mount_type()); } } } TEST_F(IsolatedContextTest, TestWithVirtualRoot) { std::string cracked_id; base::FilePath cracked_path; FileSystemMountOption cracked_option; // Trying to crack virtual root "/" returns true but with empty cracked path // as "/" of the isolated filesystem is a pure virtual directory // that has no corresponding platform directory. base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_); ASSERT_TRUE(isolated_context()->CrackVirtualPath( virtual_path, &cracked_id, NULL, NULL, &cracked_path, &cracked_option)); ASSERT_EQ(FPL(""), cracked_path.value()); ASSERT_EQ(id_, cracked_id); // Trying to crack "/foo" should fail (because "foo" is not the one // included in the kTestPaths). virtual_path = isolated_context()->CreateVirtualRootPath( id_).AppendASCII("foo"); ASSERT_FALSE(isolated_context()->CrackVirtualPath( virtual_path, &cracked_id, NULL, NULL, &cracked_path, &cracked_option)); } TEST_F(IsolatedContextTest, CanHandleURL) { const GURL test_origin("http://chromium.org"); const base::FilePath test_path(FPL("/mount")); // Should handle isolated file system. EXPECT_TRUE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeIsolated)); // Shouldn't handle the rest. EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeExternal)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeTemporary)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypePersistent)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeTest)); // Not even if it's isolated subtype. EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeNativeLocal)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeDragged)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeNativeMedia)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( fileapi::kFileSystemTypeDeviceMedia)); } TEST_F(IsolatedContextTest, VirtualFileSystemTests) { // Should be able to register empty and non-absolute paths std::string empty_fsid = isolated_context()->RegisterFileSystemForVirtualPath( fileapi::kFileSystemTypeIsolated, "_", base::FilePath()); std::string relative_fsid = isolated_context()->RegisterFileSystemForVirtualPath( fileapi::kFileSystemTypeIsolated, "_", base::FilePath(FPL("relpath"))); ASSERT_FALSE(empty_fsid.empty()); ASSERT_FALSE(relative_fsid.empty()); // Make sure that filesystem root is not prepended to cracked virtual paths. base::FilePath database_root = base::FilePath(DRIVE FPL("/database_path")); std::string database_fsid = isolated_context()->RegisterFileSystemForVirtualPath( fileapi::kFileSystemTypeIsolated, "_", database_root); base::FilePath test_virtual_path = base::FilePath().AppendASCII("virtualdir").AppendASCII("virtualfile.txt"); base::FilePath whole_virtual_path = isolated_context()->CreateVirtualRootPath(database_fsid) .AppendASCII("_").Append(test_virtual_path); std::string cracked_id; base::FilePath cracked_path; std::string cracked_inner_id; FileSystemMountOption cracked_option; ASSERT_TRUE(isolated_context()->CrackVirtualPath( whole_virtual_path, &cracked_id, NULL, &cracked_inner_id, &cracked_path, &cracked_option)); ASSERT_EQ(database_fsid, cracked_id); ASSERT_EQ(test_virtual_path, cracked_path); EXPECT_TRUE(cracked_inner_id.empty()); } } // namespace content