diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 06:15:43 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 06:15:43 +0000 |
commit | eafe2e7b3164af96be59ff2e76bc90e9c113790b (patch) | |
tree | d98279db131c2455dcd7d7ac6323a2fcca471402 /webkit/fileapi | |
parent | 9e0315287c149e48fb31d15a121699898c917b0b (diff) | |
download | chromium_src-eafe2e7b3164af96be59ff2e76bc90e9c113790b.zip chromium_src-eafe2e7b3164af96be59ff2e76bc90e9c113790b.tar.gz chromium_src-eafe2e7b3164af96be59ff2e76bc90e9c113790b.tar.bz2 |
Refactor out path-related methods from file_system_host_context into a separated module.
No functionality changes; code relocation/cleanup only.
This change is necessary to improve test coverage for another coming change
(hide the FileSystem's root directory under 'unpredictrable' location).
BUG=none
TEST=FileSystemPathManager.*
Review URL: http://codereview.chromium.org/3703003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62259 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/fileapi')
-rw-r--r-- | webkit/fileapi/file_system_path_manager.cc | 215 | ||||
-rw-r--r-- | webkit/fileapi/file_system_path_manager.h | 69 | ||||
-rw-r--r-- | webkit/fileapi/file_system_path_manager_unittest.cc | 208 | ||||
-rw-r--r-- | webkit/fileapi/webkit_fileapi.gypi | 2 |
4 files changed, 494 insertions, 0 deletions
diff --git a/webkit/fileapi/file_system_path_manager.cc b/webkit/fileapi/file_system_path_manager.cc new file mode 100644 index 0000000..12c273e --- /dev/null +++ b/webkit/fileapi/file_system_path_manager.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/fileapi/file_system_path_manager.h" + +#include "base/file_util.h" +#include "base/file_util_proxy.h" +#include "base/rand_util.h" +#include "base/logging.h" +#include "base/scoped_callback_factory.h" +#include "base/stringprintf.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "googleurl/src/gurl.h" +#include "third_party/WebKit/WebKit/chromium/public/WebCString.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFileSystem.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" +#include "webkit/glue/webkit_glue.h" + +// We use some of WebKit types for conversions between storage identifiers +// and origin URLs. +using WebKit::WebFileSystem; +using WebKit::WebSecurityOrigin; +using WebKit::WebString; + +using base::FileUtilProxy; +using base::PlatformFileError; + +namespace fileapi { + +const FilePath::CharType FileSystemPathManager::kFileSystemDirectory[] = + FILE_PATH_LITERAL("FileSystem"); + +const char FileSystemPathManager::kPersistentName[] = "Persistent"; +const char FileSystemPathManager::kTemporaryName[] = "Temporary"; + +namespace { + +// Restricted names. +// http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions +static const char* const kRestrictedNames[] = { + "con", "prn", "aux", "nul", + "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", + "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", +}; + +// Restricted chars. +static const FilePath::CharType kRestrictedChars[] = { + '/', '\\', '<', '>', ':', '?', '*', '"', '|', +}; + +inline std::string FilePathStringToASCII( + const FilePath::StringType& path_string) { +#if defined(OS_WIN) + return WideToASCII(path_string); +#elif defined(OS_POSIX) + return path_string; +#endif +} + +} // anonymous namespace + +FileSystemPathManager::FileSystemPathManager( + const FilePath& data_path, + bool is_incognito, + bool allow_file_access_from_files) + : base_path_(data_path.Append(kFileSystemDirectory)), + is_incognito_(is_incognito), + allow_file_access_from_files_(allow_file_access_from_files) { +} + +bool FileSystemPathManager::GetFileSystemRootPath( + const GURL& origin_url, fileapi::FileSystemType type, + FilePath* root_path, std::string* name) const { + // TODO(kinuko): should return an isolated temporary file system space. + if (is_incognito_) + return false; + + if (!IsAllowedScheme(origin_url)) + return false; + + std::string storage_identifier = GetStorageIdentifierFromURL(origin_url); + switch (type) { + case fileapi::kFileSystemTypeTemporary: + if (root_path) + *root_path = base_path_.AppendASCII(storage_identifier) + .AppendASCII(kTemporaryName); + if (name) + *name = storage_identifier + ":" + kTemporaryName; + return true; + case fileapi::kFileSystemTypePersistent: + if (root_path) + *root_path = base_path_.AppendASCII(storage_identifier) + .AppendASCII(kPersistentName); + if (name) + *name = storage_identifier + ":" + kPersistentName; + return true; + } + LOG(WARNING) << "Unknown filesystem type is requested:" << type; + return false; +} + +bool FileSystemPathManager::CheckValidFileSystemPath( + const FilePath& path) const { + // Any paths that includes parent references are considered invalid. + if (path.ReferencesParent()) + return false; + + // The path should be a child of the profile FileSystem path. + FilePath relative; + if (!base_path_.AppendRelativePath(path, &relative)) + return false; + + // The relative path from the profile FileSystem path should at least + // contains two components, one for storage identifier and the other for type + + std::vector<FilePath::StringType> components; + relative.GetComponents(&components); + if (components.size() < 2) + return false; + + // The second component of the relative path to the root directory + // must be kPersistent or kTemporary. + if (!IsStringASCII(components[1])) + return false; + + std::string ascii_type_component = FilePathStringToASCII(components[1]); + if (ascii_type_component != kPersistentName && + ascii_type_component != kTemporaryName) + return false; + + return true; +} + +bool FileSystemPathManager::GetOriginFromPath( + const FilePath& path, GURL* origin_url) { + DCHECK(origin_url); + FilePath relative; + if (!base_path_.AppendRelativePath(path, &relative)) { + // The path should be a child of the profile's FileSystem path. + return false; + } + std::vector<FilePath::StringType> components; + relative.GetComponents(&components); + if (components.size() < 2) { + // The relative path should at least contain storage identifier and type. + return false; + } + WebSecurityOrigin web_security_origin = + WebSecurityOrigin::createFromDatabaseIdentifier( + webkit_glue::FilePathStringToWebString(components[0])); + *origin_url = GURL(web_security_origin.toString()); + + // We need this work-around for file:/// URIs as + // createFromDatabaseIdentifier returns empty origin_url for them. + if (allow_file_access_from_files_ && origin_url->spec().empty() && + components[0].find(FILE_PATH_LITERAL("file")) == 0) { + *origin_url = GURL("file:///"); + return true; + } + + return IsAllowedScheme(*origin_url); +} + +bool FileSystemPathManager::IsAllowedScheme(const GURL& url) const { + // Basically we only accept http or https. We allow file:// URLs + // only if --allow-file-access-from-files flag is given. + return url.SchemeIs("http") || url.SchemeIs("https") || + (url.SchemeIsFile() && allow_file_access_from_files_); +} + +bool FileSystemPathManager::IsRestrictedFileName( + const FilePath& filename) const { + if (filename.value().size() == 0) + return false; + + if (IsWhitespace(filename.value()[filename.value().size() - 1]) || + filename.value()[filename.value().size() - 1] == '.') + return true; + + std::string filename_lower = StringToLowerASCII( + FilePathStringToASCII(filename.value())); + + for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { + // Exact match. + if (filename_lower == kRestrictedNames[i]) + return true; + // Starts with "RESTRICTED_NAME.". + if (filename_lower.find(std::string(kRestrictedNames[i]) + ".") == 0) + return true; + } + + for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { + if (filename.value().find(kRestrictedChars[i]) != + FilePath::StringType::npos) + return true; + } + + return false; +} + +std::string FileSystemPathManager::GetStorageIdentifierFromURL( + const GURL& url) { + WebKit::WebSecurityOrigin web_security_origin = + WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); + return web_security_origin.databaseIdentifier().utf8(); +} + +} // namespace fileapi + +COMPILE_ASSERT(int(WebFileSystem::TypeTemporary) == \ + int(fileapi::kFileSystemTypeTemporary), mismatching_enums); +COMPILE_ASSERT(int(WebFileSystem::TypePersistent) == \ + int(fileapi::kFileSystemTypePersistent), mismatching_enums); diff --git a/webkit/fileapi/file_system_path_manager.h b/webkit/fileapi/file_system_path_manager.h new file mode 100644 index 0000000..505a3c1 --- /dev/null +++ b/webkit/fileapi/file_system_path_manager.h @@ -0,0 +1,69 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_FILEAPI_FILE_SYSTEM_PATH_MANAGER_H_ +#define WEBKIT_FILEAPI_FILE_SYSTEM_PATH_MANAGER_H_ + +#include <map> + +#include "base/callback.h" +#include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" +#include "webkit/fileapi/file_system_types.h" + +namespace fileapi { + +class FileSystemPathManager { + public: + FileSystemPathManager(const FilePath& data_path, + bool is_incognito, + bool allow_file_access_from_files); + + // Returns the root path and name for the file system specified by given + // |origin_url| and |type|. Returns true if the file system is available + // for the profile and |root_path| and |name| are filled successfully. + bool GetFileSystemRootPath(const GURL& origin_url, + fileapi::FileSystemType type, + FilePath* root_path, + std::string* name) const; + + // Checks if a given |path| is in the FileSystem base directory. + bool CheckValidFileSystemPath(const FilePath& path) const; + + // Retrieves the origin URL for the given |path| and populates + // |origin_url|. It returns false when the given |path| is not a + // valid filesystem path. + bool GetOriginFromPath(const FilePath& path, GURL* origin_url); + + // Returns true if the given |url|'s scheme is allowed to access + // filesystem. + bool IsAllowedScheme(const GURL& url) const; + + // Checks if a given |filename| contains any restricted names/chars in it. + bool IsRestrictedFileName(const FilePath& filename) const; + + // The FileSystem directory name. + static const FilePath::CharType kFileSystemDirectory[]; + + static const char kPersistentName[]; + static const char kTemporaryName[]; + + private: + class GetFileSystemRootPathTask; + friend class GetFileSystemRootPathTask; + + // Returns the storage identifier string for the given |url|. + static std::string GetStorageIdentifierFromURL(const GURL& url); + + const FilePath base_path_; + const bool is_incognito_; + bool allow_file_access_from_files_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemPathManager); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_FILE_SYSTEM_PATH_MANAGER_H_ diff --git a/webkit/fileapi/file_system_path_manager_unittest.cc b/webkit/fileapi/file_system_path_manager_unittest.cc new file mode 100644 index 0000000..1e67c3a --- /dev/null +++ b/webkit/fileapi/file_system_path_manager_unittest.cc @@ -0,0 +1,208 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/fileapi/file_system_path_manager.h" + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "base/scoped_temp_dir.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +using fileapi::FileSystemPathManager; + +namespace { + +// PS stands for path separator. +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +#define PS "\\" +#else +#define PS "/" +#endif + +const FilePath::CharType kTestDataPath[] = FILE_PATH_LITERAL( + "//tmp/TestingProfilePath"); + +const struct RootPathTest { + fileapi::FileSystemType type; + bool off_the_record; + const char* origin_url; + bool expect_root_path; + const char* expected_path; +} kRootPathTestCases[] = { + { fileapi::kFileSystemTypeTemporary, false, "http://host:1/", + true, "FileSystem" PS "http_host_1" PS "Temporary" }, + { fileapi::kFileSystemTypePersistent, false, "http://host:2/", + true, "FileSystem" PS "http_host_2" PS "Persistent" }, + { fileapi::kFileSystemTypeTemporary, true, "http://host:3/", + false, "" }, + { fileapi::kFileSystemTypePersistent, true, "http://host:4/", + false, "" }, + // We disallow file:// URIs to access filesystem. + { fileapi::kFileSystemTypeTemporary, false, "file:///some/path", false, "" }, + { fileapi::kFileSystemTypePersistent, false, "file:///some/path", false, "" }, +}; + +const struct CheckValidPathTest { + FilePath::StringType path; + bool expected_valid; +} kCheckValidPathTestCases[] = { + { FILE_PATH_LITERAL("//tmp/foo.txt"), false, }, + { FILE_PATH_LITERAL("//etc/hosts"), false, }, + { FILE_PATH_LITERAL("foo.txt"), true, }, + { FILE_PATH_LITERAL("a/b/c"), true, }, + // Any paths that includes parent references are considered invalid. + { FILE_PATH_LITERAL(".."), false, }, + { FILE_PATH_LITERAL("tmp/.."), false, }, + { FILE_PATH_LITERAL("a/b/../c/.."), false, }, +}; + +const struct IsRestrictedNameTest { + FilePath::StringType name; + bool expected_dangerous; +} kIsRestrictedNameTestCases[] = { + // Name that has restricted names in it. + { FILE_PATH_LITERAL("con"), true, }, + { FILE_PATH_LITERAL("Con.txt"), true, }, + { FILE_PATH_LITERAL("Prn.png"), true, }, + { FILE_PATH_LITERAL("AUX"), true, }, + { FILE_PATH_LITERAL("nUl."), true, }, + { FILE_PATH_LITERAL("coM1"), true, }, + { FILE_PATH_LITERAL("COM3.com"), true, }, + { FILE_PATH_LITERAL("cOM7"), true, }, + { FILE_PATH_LITERAL("com9"), true, }, + { FILE_PATH_LITERAL("lpT1"), true, }, + { FILE_PATH_LITERAL("LPT4.com"), true, }, + { FILE_PATH_LITERAL("lPT8"), true, }, + { FILE_PATH_LITERAL("lPT9"), true, }, + // Similar but safe cases. + { FILE_PATH_LITERAL("con3"), false, }, + { FILE_PATH_LITERAL("PrnImage.png"), false, }, + { FILE_PATH_LITERAL("AUXX"), false, }, + { FILE_PATH_LITERAL("NULL"), false, }, + { FILE_PATH_LITERAL("coM0"), false, }, + { FILE_PATH_LITERAL("COM.com"), false, }, + { FILE_PATH_LITERAL("lpT0"), false, }, + { FILE_PATH_LITERAL("LPT.com"), false, }, + + // Ends with period or whitespace. + { FILE_PATH_LITERAL("b "), true, }, + { FILE_PATH_LITERAL("b\t"), true, }, + { FILE_PATH_LITERAL("b\n"), true, }, + { FILE_PATH_LITERAL("b\r\n"), true, }, + { FILE_PATH_LITERAL("b."), true, }, + { FILE_PATH_LITERAL("b.."), true, }, + // Similar but safe cases. + { FILE_PATH_LITERAL("b c"), false, }, + { FILE_PATH_LITERAL("b\tc"), false, }, + { FILE_PATH_LITERAL("b\nc"), false, }, + { FILE_PATH_LITERAL("b\r\nc"), false, }, + { FILE_PATH_LITERAL("b c d e f"), false, }, + { FILE_PATH_LITERAL("b.c"), false, }, + { FILE_PATH_LITERAL("b..c"), false, }, + + // Name that has restricted chars in it. + { FILE_PATH_LITERAL("a\\b"), true, }, + { FILE_PATH_LITERAL("a/b"), true, }, + { FILE_PATH_LITERAL("a<b"), true, }, + { FILE_PATH_LITERAL("a>b"), true, }, + { FILE_PATH_LITERAL("a:b"), true, }, + { FILE_PATH_LITERAL("a?b"), true, }, + { FILE_PATH_LITERAL("a|b"), true, }, + { FILE_PATH_LITERAL("ab\\"), true, }, + { FILE_PATH_LITERAL("ab/.txt"), true, }, + { FILE_PATH_LITERAL("ab<.txt"), true, }, + { FILE_PATH_LITERAL("ab>.txt"), true, }, + { FILE_PATH_LITERAL("ab:.txt"), true, }, + { FILE_PATH_LITERAL("ab?.txt"), true, }, + { FILE_PATH_LITERAL("ab|.txt"), true, }, + { FILE_PATH_LITERAL("\\ab"), true, }, + { FILE_PATH_LITERAL("/ab"), true, }, + { FILE_PATH_LITERAL("<ab"), true, }, + { FILE_PATH_LITERAL(">ab"), true, }, + { FILE_PATH_LITERAL(":ab"), true, }, + { FILE_PATH_LITERAL("?ab"), true, }, + { FILE_PATH_LITERAL("|ab"), true, }, +}; + +} // namespace + +class FileSystemPathManagerTest : public testing::Test { + public: + FileSystemPathManagerTest() + : data_path_(kTestDataPath) { + } + + FileSystemPathManager* GetNewPathManager(bool incognito) { + path_manager_.reset(new FileSystemPathManager( + data_path_, incognito, false /* allow_file_access */)); + return path_manager_.get(); + } + + private: + FilePath data_path_; + scoped_ptr<FileSystemPathManager> path_manager_; +}; + +TEST_F(FileSystemPathManagerTest, GetRootPath) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { + SCOPED_TRACE(testing::Message() << "RootPath #" << i << " " + << kRootPathTestCases[i].expected_path); + + FileSystemPathManager* manager = GetNewPathManager( + kRootPathTestCases[i].off_the_record); + + FilePath root_path; + bool result = manager->GetFileSystemRootPath( + GURL(kRootPathTestCases[i].origin_url), + kRootPathTestCases[i].type, + &root_path, NULL); + EXPECT_EQ(kRootPathTestCases[i].expect_root_path, result); + if (result) { + FilePath expected = FilePath(kTestDataPath).AppendASCII( + kRootPathTestCases[i].expected_path); + EXPECT_EQ(expected.value(), root_path.value()); + } + } +} + +TEST_F(FileSystemPathManagerTest, CheckValidPath) { + FileSystemPathManager* manager = GetNewPathManager(false); + FilePath root_path; + EXPECT_TRUE(manager->GetFileSystemRootPath( + GURL("http://foo.com/"), fileapi::kFileSystemTypePersistent, + &root_path, NULL)); + + // The root path must be valid, but upper directories or directories + // that are not in our temporary or persistent directory must be + // evaluated invalid. + EXPECT_TRUE(manager->CheckValidFileSystemPath(root_path)); + EXPECT_FALSE(manager->CheckValidFileSystemPath(root_path.DirName())); + EXPECT_FALSE(manager->CheckValidFileSystemPath( + root_path.DirName().DirName())); + EXPECT_FALSE(manager->CheckValidFileSystemPath( + root_path.DirName().AppendASCII("ArbitraryName"))); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCheckValidPathTestCases); ++i) { + SCOPED_TRACE(testing::Message() << "CheckValidPath #" << i << " " + << kCheckValidPathTestCases[i].path); + FilePath path(kCheckValidPathTestCases[i].path); + if (!path.IsAbsolute()) + path = root_path.Append(path); + EXPECT_EQ(kCheckValidPathTestCases[i].expected_valid, + manager->CheckValidFileSystemPath(path)); + } +} + +TEST_F(FileSystemPathManagerTest, IsRestrictedName) { + FileSystemPathManager* manager = GetNewPathManager(false); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kIsRestrictedNameTestCases); ++i) { + SCOPED_TRACE(testing::Message() << "IsRestrictedName #" << i << " " + << kIsRestrictedNameTestCases[i].name); + FilePath name(kIsRestrictedNameTestCases[i].name); + EXPECT_EQ(kIsRestrictedNameTestCases[i].expected_dangerous, + manager->IsRestrictedFileName(name)); + } +} diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index f01e9db..5189564 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -17,6 +17,8 @@ 'file_system_callback_dispatcher.h', 'file_system_operation.cc', 'file_system_operation.h', + 'file_system_path_manager.cc', + 'file_system_path_manager.h', 'file_system_quota.cc', 'file_system_quota.h', 'file_system_types.h', |