1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "storage/browser/file_system/file_system_context.h"
6
7 #include <stddef.h>
8
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/macros.h"
11 #include "base/stl_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/test/task_environment.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "build/build_config.h"
16 #include "storage/browser/file_system/external_mount_points.h"
17 #include "storage/browser/file_system/file_system_backend.h"
18 #include "storage/browser/file_system/isolated_context.h"
19 #include "storage/browser/test/mock_quota_manager.h"
20 #include "storage/browser/test/mock_special_storage_policy.h"
21 #include "storage/browser/test/test_file_system_options.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 #define FPL(x) FILE_PATH_LITERAL(x)
25
26 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
27 #define DRIVE FPL("C:")
28 #else
29 #define DRIVE
30 #endif
31
32 namespace storage {
33
34 namespace {
35
36 const char kTestOrigin[] = "http://chromium.org/";
37
CreateRawFileSystemURL(const std::string & type_str,const std::string & fs_id)38 GURL CreateRawFileSystemURL(const std::string& type_str,
39 const std::string& fs_id) {
40 std::string url_str =
41 base::StringPrintf("filesystem:http://chromium.org/%s/%s/root/file",
42 type_str.c_str(), fs_id.c_str());
43 return GURL(url_str);
44 }
45
46 class FileSystemContextTest : public testing::Test {
47 public:
48 FileSystemContextTest() = default;
49
SetUp()50 void SetUp() override {
51 ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
52
53 storage_policy_ = new MockSpecialStoragePolicy();
54
55 mock_quota_manager_ = new MockQuotaManager(
56 false /* is_incognito */, data_dir_.GetPath(),
57 base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get());
58 }
59
60 protected:
CreateFileSystemContextForTest(ExternalMountPoints * external_mount_points)61 FileSystemContext* CreateFileSystemContextForTest(
62 ExternalMountPoints* external_mount_points) {
63 return new FileSystemContext(
64 base::ThreadTaskRunnerHandle::Get().get(),
65 base::ThreadTaskRunnerHandle::Get().get(), external_mount_points,
66 storage_policy_.get(), mock_quota_manager_->proxy(),
67 std::vector<std::unique_ptr<FileSystemBackend>>(),
68 std::vector<URLRequestAutoMountHandler>(), data_dir_.GetPath(),
69 CreateAllowFileAccessOptions());
70 }
71
72 // Verifies a *valid* filesystem url has expected values.
ExpectFileSystemURLMatches(const FileSystemURL & url,const GURL & expect_origin,FileSystemType expect_mount_type,FileSystemType expect_type,const base::FilePath & expect_path,const base::FilePath & expect_virtual_path,const std::string & expect_filesystem_id)73 void ExpectFileSystemURLMatches(const FileSystemURL& url,
74 const GURL& expect_origin,
75 FileSystemType expect_mount_type,
76 FileSystemType expect_type,
77 const base::FilePath& expect_path,
78 const base::FilePath& expect_virtual_path,
79 const std::string& expect_filesystem_id) {
80 EXPECT_TRUE(url.is_valid());
81
82 EXPECT_EQ(expect_origin, url.origin().GetURL());
83 EXPECT_EQ(expect_mount_type, url.mount_type());
84 EXPECT_EQ(expect_type, url.type());
85 EXPECT_EQ(expect_path, url.path());
86 EXPECT_EQ(expect_virtual_path, url.virtual_path());
87 EXPECT_EQ(expect_filesystem_id, url.filesystem_id());
88 }
89
90 private:
91 base::ScopedTempDir data_dir_;
92 base::test::TaskEnvironment task_environment_;
93 scoped_refptr<SpecialStoragePolicy> storage_policy_;
94 scoped_refptr<MockQuotaManager> mock_quota_manager_;
95 };
96
97 // It is not valid to pass nullptr ExternalMountPoints to FileSystemContext on
98 // ChromeOS.
99 #if !defined(OS_CHROMEOS)
TEST_F(FileSystemContextTest,NullExternalMountPoints)100 TEST_F(FileSystemContextTest, NullExternalMountPoints) {
101 scoped_refptr<FileSystemContext> file_system_context(
102 CreateFileSystemContextForTest(nullptr));
103
104 // Cracking system external mount and isolated mount points should work.
105 std::string isolated_name = "root";
106 IsolatedContext::ScopedFSHandle isolated_fs =
107 IsolatedContext::GetInstance()->RegisterFileSystemForPath(
108 kFileSystemTypeNativeLocal, std::string(),
109 base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_name);
110 std::string isolated_id = isolated_fs.id();
111 // Register system external mount point.
112 ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
113 "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
114 base::FilePath(DRIVE FPL("/test/sys/"))));
115
116 FileSystemURL cracked_isolated = file_system_context->CrackURL(
117 CreateRawFileSystemURL("isolated", isolated_id));
118
119 ExpectFileSystemURLMatches(
120 cracked_isolated, GURL(kTestOrigin), kFileSystemTypeIsolated,
121 kFileSystemTypeNativeLocal,
122 base::FilePath(DRIVE FPL("/test/isolated/root/file"))
123 .NormalizePathSeparators(),
124 base::FilePath::FromUTF8Unsafe(isolated_id)
125 .Append(FPL("root/file"))
126 .NormalizePathSeparators(),
127 isolated_id);
128
129 FileSystemURL cracked_external = file_system_context->CrackURL(
130 CreateRawFileSystemURL("external", "system"));
131
132 ExpectFileSystemURLMatches(
133 cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal,
134 kFileSystemTypeNativeLocal,
135 base::FilePath(DRIVE FPL("/test/sys/root/file"))
136 .NormalizePathSeparators(),
137 base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
138 "system");
139
140 IsolatedContext::GetInstance()->RevokeFileSystem(isolated_id);
141 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
142 }
143 #endif // !defiend(OS_CHROMEOS)
144
TEST_F(FileSystemContextTest,FileSystemContextKeepsMountPointsAlive)145 TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) {
146 scoped_refptr<ExternalMountPoints> mount_points =
147 ExternalMountPoints::CreateRefCounted();
148
149 // Register system external mount point.
150 ASSERT_TRUE(mount_points->RegisterFileSystem(
151 "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
152 base::FilePath(DRIVE FPL("/test/sys/"))));
153
154 scoped_refptr<FileSystemContext> file_system_context(
155 CreateFileSystemContextForTest(mount_points.get()));
156
157 // Release a MountPoints reference created in the test.
158 mount_points = nullptr;
159
160 // FileSystemContext should keep a reference to the |mount_points|, so it
161 // should be able to resolve the URL.
162 FileSystemURL cracked_external = file_system_context->CrackURL(
163 CreateRawFileSystemURL("external", "system"));
164
165 ExpectFileSystemURLMatches(
166 cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal,
167 kFileSystemTypeNativeLocal,
168 base::FilePath(DRIVE FPL("/test/sys/root/file"))
169 .NormalizePathSeparators(),
170 base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
171 "system");
172
173 // No need to revoke the registered filesystem since |mount_points| lifetime
174 // is bound to this test.
175 }
176
TEST_F(FileSystemContextTest,CrackFileSystemURL)177 TEST_F(FileSystemContextTest, CrackFileSystemURL) {
178 scoped_refptr<ExternalMountPoints> external_mount_points(
179 ExternalMountPoints::CreateRefCounted());
180 scoped_refptr<FileSystemContext> file_system_context(
181 CreateFileSystemContextForTest(external_mount_points.get()));
182
183 // Register an isolated mount point.
184 std::string isolated_file_system_name = "root";
185 IsolatedContext::ScopedFSHandle isolated_fs =
186 IsolatedContext::GetInstance()->RegisterFileSystemForPath(
187 kFileSystemTypeNativeLocal, std::string(),
188 base::FilePath(DRIVE FPL("/test/isolated/root")),
189 &isolated_file_system_name);
190 const std::string kIsolatedFileSystemID = isolated_fs.id();
191 // Register system external mount point.
192 ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
193 "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
194 base::FilePath(DRIVE FPL("/test/sys/"))));
195 ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
196 "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(),
197 base::FilePath(DRIVE FPL("/test/ext"))));
198 // Register a system external mount point with the same name/id as the
199 // registered isolated mount point.
200 ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
201 kIsolatedFileSystemID, kFileSystemTypeRestrictedNativeLocal,
202 FileSystemMountOption(),
203 base::FilePath(DRIVE FPL("/test/system/isolated"))));
204 // Add a mount points with the same name as a system mount point to
205 // FileSystemContext's external mount points.
206 ASSERT_TRUE(external_mount_points->RegisterFileSystem(
207 "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(),
208 base::FilePath(DRIVE FPL("/test/local/ext/"))));
209
210 const GURL kTestOrigin = GURL("http://chromium.org/");
211 const base::FilePath kVirtualPathNoRoot = base::FilePath(FPL("root/file"));
212
213 struct TestCase {
214 // Test case values.
215 std::string root;
216 std::string type_str;
217
218 // Expected test results.
219 bool expect_is_valid;
220 FileSystemType expect_mount_type;
221 FileSystemType expect_type;
222 const base::FilePath::CharType* expect_path;
223 std::string expect_filesystem_id;
224 };
225
226 const TestCase kTestCases[] = {
227 // Following should not be handled by the url crackers:
228 {
229 "pers_mount", "persistent", true /* is_valid */,
230 kFileSystemTypePersistent, kFileSystemTypePersistent,
231 FPL("pers_mount/root/file"), std::string() /* filesystem id */
232 },
233 {
234 "temp_mount", "temporary", true /* is_valid */,
235 kFileSystemTypeTemporary, kFileSystemTypeTemporary,
236 FPL("temp_mount/root/file"), std::string() /* filesystem id */
237 },
238 // Should be cracked by isolated mount points:
239 {kIsolatedFileSystemID, "isolated", true /* is_valid */,
240 kFileSystemTypeIsolated, kFileSystemTypeNativeLocal,
241 DRIVE FPL("/test/isolated/root/file"), kIsolatedFileSystemID},
242 // Should be cracked by system mount points:
243 {"system", "external", true /* is_valid */, kFileSystemTypeExternal,
244 kFileSystemTypeNativeLocal, DRIVE FPL("/test/sys/root/file"), "system"},
245 {kIsolatedFileSystemID, "external", true /* is_valid */,
246 kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal,
247 DRIVE FPL("/test/system/isolated/root/file"), kIsolatedFileSystemID},
248 // Should be cracked by FileSystemContext's ExternalMountPoints.
249 {"ext", "external", true /* is_valid */, kFileSystemTypeExternal,
250 kFileSystemTypeNativeLocal, DRIVE FPL("/test/local/ext/root/file"),
251 "ext"},
252 // Test for invalid filesystem url (made invalid by adding invalid
253 // filesystem type).
254 {"sytem", "external", false /* is_valid */,
255 // The rest of values will be ignored.
256 kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""), std::string()},
257 // Test for URL with non-existing filesystem id.
258 {"invalid", "external", false /* is_valid */,
259 // The rest of values will be ignored.
260 kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""), std::string()},
261 };
262
263 for (size_t i = 0; i < base::size(kTestCases); ++i) {
264 const base::FilePath virtual_path =
265 base::FilePath::FromUTF8Unsafe(kTestCases[i].root)
266 .Append(kVirtualPathNoRoot);
267
268 GURL raw_url =
269 CreateRawFileSystemURL(kTestCases[i].type_str, kTestCases[i].root);
270 FileSystemURL cracked_url = file_system_context->CrackURL(raw_url);
271
272 SCOPED_TRACE(testing::Message() << "Test case " << i << ": "
273 << "Cracking URL: " << raw_url);
274
275 EXPECT_EQ(kTestCases[i].expect_is_valid, cracked_url.is_valid());
276 if (!kTestCases[i].expect_is_valid)
277 continue;
278
279 ExpectFileSystemURLMatches(
280 cracked_url, GURL(kTestOrigin), kTestCases[i].expect_mount_type,
281 kTestCases[i].expect_type,
282 base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(),
283 virtual_path.NormalizePathSeparators(),
284 kTestCases[i].expect_filesystem_id);
285 }
286
287 IsolatedContext::GetInstance()->RevokeFileSystemByPath(
288 base::FilePath(DRIVE FPL("/test/isolated/root")));
289 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
290 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("ext");
291 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
292 kIsolatedFileSystemID);
293 }
294
TEST_F(FileSystemContextTest,CanServeURLRequest)295 TEST_F(FileSystemContextTest, CanServeURLRequest) {
296 scoped_refptr<ExternalMountPoints> external_mount_points(
297 ExternalMountPoints::CreateRefCounted());
298 scoped_refptr<FileSystemContext> context(
299 CreateFileSystemContextForTest(external_mount_points.get()));
300
301 // A request for a sandbox mount point should be served.
302 FileSystemURL cracked_url =
303 context->CrackURL(CreateRawFileSystemURL("persistent", "pers_mount"));
304 EXPECT_EQ(kFileSystemTypePersistent, cracked_url.mount_type());
305 EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
306
307 // A request for an isolated mount point should NOT be served.
308 std::string isolated_fs_name = "root";
309 IsolatedContext::ScopedFSHandle isolated_fs =
310 IsolatedContext::GetInstance()->RegisterFileSystemForPath(
311 kFileSystemTypeNativeLocal, std::string(),
312 base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_fs_name);
313 std::string isolated_fs_id = isolated_fs.id();
314 cracked_url =
315 context->CrackURL(CreateRawFileSystemURL("isolated", isolated_fs_id));
316 EXPECT_EQ(kFileSystemTypeIsolated, cracked_url.mount_type());
317 EXPECT_FALSE(context->CanServeURLRequest(cracked_url));
318
319 // A request for an external mount point should be served.
320 const std::string kExternalMountName = "ext_mount";
321 ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
322 kExternalMountName, kFileSystemTypeNativeLocal, FileSystemMountOption(),
323 base::FilePath()));
324 cracked_url =
325 context->CrackURL(CreateRawFileSystemURL("external", kExternalMountName));
326 EXPECT_EQ(kFileSystemTypeExternal, cracked_url.mount_type());
327 EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
328
329 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
330 kExternalMountName);
331 IsolatedContext::GetInstance()->RevokeFileSystem(isolated_fs_id);
332 }
333
334 // Ensures that a backend exists for each common isolated file system type.
335 // See http://crbug.com/447027
TEST_F(FileSystemContextTest,IsolatedFileSystemsTypesHandled)336 TEST_F(FileSystemContextTest, IsolatedFileSystemsTypesHandled) {
337 // This does not provide any "additional" file system handlers. In particular,
338 // on Chrome OS it does not provide chromeos::FileSystemBackend.
339 scoped_refptr<FileSystemContext> file_system_context(
340 CreateFileSystemContextForTest(nullptr));
341
342 // Isolated file system types are handled.
343 EXPECT_TRUE(
344 file_system_context->GetFileSystemBackend(kFileSystemTypeIsolated));
345 EXPECT_TRUE(
346 file_system_context->GetFileSystemBackend(kFileSystemTypeDragged));
347 EXPECT_TRUE(file_system_context->GetFileSystemBackend(
348 kFileSystemTypeForTransientFile));
349 EXPECT_TRUE(
350 file_system_context->GetFileSystemBackend(kFileSystemTypeNativeLocal));
351 EXPECT_TRUE(file_system_context->GetFileSystemBackend(
352 kFileSystemTypeNativeForPlatformApp));
353 }
354
355 } // namespace
356
357 } // namespace storage
358