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