summaryrefslogtreecommitdiffstats
path: root/webkit/fileapi
diff options
context:
space:
mode:
authorkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-12 06:15:43 +0000
committerkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-12 06:15:43 +0000
commiteafe2e7b3164af96be59ff2e76bc90e9c113790b (patch)
treed98279db131c2455dcd7d7ac6323a2fcca471402 /webkit/fileapi
parent9e0315287c149e48fb31d15a121699898c917b0b (diff)
downloadchromium_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.cc215
-rw-r--r--webkit/fileapi/file_system_path_manager.h69
-rw-r--r--webkit/fileapi/file_system_path_manager_unittest.cc208
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi2
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',