1 // Copyright 2014 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 <stddef.h>
6
7 #include <string>
8
9 #include "base/macros.h"
10 #include "base/stl_util.h"
11 #include "storage/browser/file_system/file_system_url.h"
12 #include "storage/browser/file_system/isolated_context.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 #define FPL(x) FILE_PATH_LITERAL(x)
16
17 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
18 #define DRIVE FPL("C:")
19 #else
20 #define DRIVE
21 #endif
22
23 namespace storage {
24
25 using FileInfo = IsolatedContext::MountPointInfo;
26
27 namespace {
28
29 const base::FilePath kTestPaths[] = {
30 base::FilePath(DRIVE FPL("/a/b.txt")),
31 base::FilePath(DRIVE FPL("/c/d/e")),
32 base::FilePath(DRIVE FPL("/h/")),
33 base::FilePath(DRIVE FPL("/")),
34 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
35 base::FilePath(DRIVE FPL("\\foo\\bar")),
36 base::FilePath(DRIVE FPL("\\")),
37 #endif
38 // For duplicated base name test.
39 base::FilePath(DRIVE FPL("/")),
40 base::FilePath(DRIVE FPL("/f/e")),
41 base::FilePath(DRIVE FPL("/f/b.txt")),
42 };
43
44 } // namespace
45
46 class IsolatedContextTest : public testing::Test {
47 public:
IsolatedContextTest()48 IsolatedContextTest() {
49 for (const auto& path : kTestPaths)
50 fileset_.insert(path.NormalizePathSeparators());
51 }
52
SetUp()53 void SetUp() override {
54 IsolatedContext::FileInfoSet files;
55 for (const auto& path : kTestPaths) {
56 std::string name;
57 ASSERT_TRUE(files.AddPath(path.NormalizePathSeparators(), &name));
58 names_.push_back(name);
59 }
60 id_ = IsolatedContext::GetInstance()->RegisterDraggedFileSystem(files);
61 IsolatedContext::GetInstance()->AddReference(id_);
62 ASSERT_FALSE(id_.empty());
63 }
64
TearDown()65 void TearDown() override {
66 IsolatedContext::GetInstance()->RemoveReference(id_);
67 }
68
isolated_context() const69 IsolatedContext* isolated_context() const {
70 return IsolatedContext::GetInstance();
71 }
72
73 protected:
74 std::string id_;
75 std::multiset<base::FilePath> fileset_;
76 std::vector<std::string> names_;
77
78 private:
79 DISALLOW_COPY_AND_ASSIGN(IsolatedContextTest);
80 };
81
TEST_F(IsolatedContextTest,RegisterAndRevokeTest)82 TEST_F(IsolatedContextTest, RegisterAndRevokeTest) {
83 // See if the returned top-level entries match with what we registered.
84 std::vector<FileInfo> toplevels;
85 ASSERT_TRUE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
86 ASSERT_EQ(fileset_.size(), toplevels.size());
87 for (size_t i = 0; i < toplevels.size(); ++i) {
88 ASSERT_TRUE(fileset_.find(toplevels[i].path) != fileset_.end());
89 }
90
91 // See if the name of each registered kTestPaths (that is what we
92 // register in SetUp() by RegisterDraggedFileSystem) is properly cracked as
93 // a valid virtual path in the isolated filesystem.
94 for (size_t i = 0; i < base::size(kTestPaths); ++i) {
95 base::FilePath virtual_path =
96 isolated_context()->CreateVirtualRootPath(id_).AppendASCII(names_[i]);
97 std::string cracked_id;
98 base::FilePath cracked_path;
99 std::string cracked_inner_id;
100 FileSystemType cracked_type;
101 FileSystemMountOption cracked_option;
102 ASSERT_TRUE(isolated_context()->CrackVirtualPath(
103 virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
104 &cracked_path, &cracked_option));
105 ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(),
106 cracked_path.value());
107 ASSERT_EQ(id_, cracked_id);
108 ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
109 EXPECT_TRUE(cracked_inner_id.empty());
110 }
111
112 // Make sure GetRegisteredPath returns false for id_ since it is
113 // registered for dragged files.
114 base::FilePath path;
115 ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
116
117 // Deref the current one and registering a new one.
118 isolated_context()->RemoveReference(id_);
119
120 IsolatedContext::ScopedFSHandle fs2 =
121 isolated_context()->RegisterFileSystemForPath(
122 kFileSystemTypeNativeLocal, std::string(),
123 base::FilePath(DRIVE FPL("/foo")), nullptr);
124
125 // Make sure the GetDraggedFileInfo returns false for both ones.
126 ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(fs2.id(), &toplevels));
127 ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
128
129 // Make sure the GetRegisteredPath returns true only for the new one.
130 ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
131 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
132
133 // Try registering three more file systems for the same path as id2.
134 IsolatedContext::ScopedFSHandle fs3 =
135 isolated_context()->RegisterFileSystemForPath(
136 kFileSystemTypeNativeLocal, std::string(), path, nullptr);
137 IsolatedContext::ScopedFSHandle fs4 =
138 isolated_context()->RegisterFileSystemForPath(
139 kFileSystemTypeNativeLocal, std::string(), path, nullptr);
140 IsolatedContext::ScopedFSHandle fs5 =
141 isolated_context()->RegisterFileSystemForPath(
142 kFileSystemTypeNativeLocal, std::string(), path, nullptr);
143
144 // Remove file system for id4.
145 fs4 = IsolatedContext::ScopedFSHandle();
146
147 // Only id4 should become invalid now.
148 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
149 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
150 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
151 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
152
153 // Revoke file system id5, after adding multiple references.
154 isolated_context()->AddReference(fs5.id());
155 isolated_context()->AddReference(fs5.id());
156 isolated_context()->AddReference(fs5.id());
157 isolated_context()->RevokeFileSystem(fs5.id());
158
159 // No matter how many references we add id5 must be invalid now.
160 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
161 ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
162 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
163 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
164
165 // Revoke the file systems by path.
166 isolated_context()->RevokeFileSystemByPath(path);
167
168 // Now all the file systems associated to the path must be invalid.
169 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
170 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
171 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
172 ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
173 }
174
TEST_F(IsolatedContextTest,CrackWithRelativePaths)175 TEST_F(IsolatedContextTest, CrackWithRelativePaths) {
176 const struct {
177 base::FilePath::StringType path;
178 bool valid;
179 } relatives[] = {
180 {FPL("foo"), true},
181 {FPL("foo/bar"), true},
182 {FPL(".."), false},
183 {FPL("foo/.."), false},
184 {FPL("foo/../bar"), false},
185 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
186 #define SHOULD_FAIL_WITH_WIN_SEPARATORS false
187 #else
188 #define SHOULD_FAIL_WITH_WIN_SEPARATORS true
189 #endif
190 {FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS},
191 {FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS},
192 };
193
194 for (size_t i = 0; i < base::size(kTestPaths); ++i) {
195 for (size_t j = 0; j < base::size(relatives); ++j) {
196 SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value()
197 << " " << relatives[j].path);
198 base::FilePath virtual_path = isolated_context()
199 ->CreateVirtualRootPath(id_)
200 .AppendASCII(names_[i])
201 .Append(relatives[j].path);
202 std::string cracked_id;
203 base::FilePath cracked_path;
204 FileSystemType cracked_type;
205 std::string cracked_inner_id;
206 FileSystemMountOption cracked_option;
207 if (!relatives[j].valid) {
208 ASSERT_FALSE(isolated_context()->CrackVirtualPath(
209 virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
210 &cracked_path, &cracked_option));
211 continue;
212 }
213 ASSERT_TRUE(isolated_context()->CrackVirtualPath(
214 virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
215 &cracked_path, &cracked_option));
216 ASSERT_EQ(kTestPaths[i]
217 .Append(relatives[j].path)
218 .NormalizePathSeparators()
219 .value(),
220 cracked_path.value());
221 ASSERT_EQ(id_, cracked_id);
222 ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
223 EXPECT_TRUE(cracked_inner_id.empty());
224 }
225 }
226 }
227
TEST_F(IsolatedContextTest,CrackURLWithRelativePaths)228 TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) {
229 const struct {
230 base::FilePath::StringType path;
231 bool valid;
232 } relatives[] = {
233 {FPL("foo"), true},
234 {FPL("foo/bar"), true},
235 {FPL(".."), false},
236 {FPL("foo/.."), false},
237 {FPL("foo/../bar"), false},
238 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
239 #define SHOULD_FAIL_WITH_WIN_SEPARATORS false
240 #else
241 #define SHOULD_FAIL_WITH_WIN_SEPARATORS true
242 #endif
243 {FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS},
244 {FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS},
245 };
246
247 for (size_t i = 0; i < base::size(kTestPaths); ++i) {
248 for (size_t j = 0; j < base::size(relatives); ++j) {
249 SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value()
250 << " " << relatives[j].path);
251 base::FilePath virtual_path = isolated_context()
252 ->CreateVirtualRootPath(id_)
253 .AppendASCII(names_[i])
254 .Append(relatives[j].path);
255
256 FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL(
257 url::Origin::Create(GURL("http://chromium.org")),
258 kFileSystemTypeIsolated, virtual_path);
259
260 ASSERT_EQ(relatives[j].valid, cracked.is_valid());
261
262 if (!relatives[j].valid)
263 continue;
264 ASSERT_EQ("http://chromium.org", cracked.origin().Serialize());
265 ASSERT_EQ(kTestPaths[i]
266 .Append(relatives[j].path)
267 .NormalizePathSeparators()
268 .value(),
269 cracked.path().value());
270 ASSERT_EQ(virtual_path.NormalizePathSeparators(), cracked.virtual_path());
271 ASSERT_EQ(id_, cracked.filesystem_id());
272 ASSERT_EQ(kFileSystemTypeDragged, cracked.type());
273 ASSERT_EQ(kFileSystemTypeIsolated, cracked.mount_type());
274 }
275 }
276 }
277
TEST_F(IsolatedContextTest,TestWithVirtualRoot)278 TEST_F(IsolatedContextTest, TestWithVirtualRoot) {
279 std::string cracked_id;
280 base::FilePath cracked_path;
281 FileSystemMountOption cracked_option;
282
283 // Trying to crack virtual root "/" returns true but with empty cracked path
284 // as "/" of the isolated filesystem is a pure virtual directory
285 // that has no corresponding platform directory.
286 base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_);
287 ASSERT_TRUE(isolated_context()->CrackVirtualPath(
288 virtual_path, &cracked_id, nullptr, nullptr, &cracked_path,
289 &cracked_option));
290 ASSERT_EQ(FPL(""), cracked_path.value());
291 ASSERT_EQ(id_, cracked_id);
292
293 // Trying to crack "/foo" should fail (because "foo" is not the one
294 // included in the kTestPaths).
295 virtual_path =
296 isolated_context()->CreateVirtualRootPath(id_).AppendASCII("foo");
297 ASSERT_FALSE(isolated_context()->CrackVirtualPath(
298 virtual_path, &cracked_id, nullptr, nullptr, &cracked_path,
299 &cracked_option));
300 }
301
TEST_F(IsolatedContextTest,CanHandleURL)302 TEST_F(IsolatedContextTest, CanHandleURL) {
303 const GURL test_origin("http://chromium.org");
304 const base::FilePath test_path(FPL("/mount"));
305
306 // Should handle isolated file system.
307 EXPECT_TRUE(
308 isolated_context()->HandlesFileSystemMountType(kFileSystemTypeIsolated));
309
310 // Shouldn't handle the rest.
311 EXPECT_FALSE(
312 isolated_context()->HandlesFileSystemMountType(kFileSystemTypeExternal));
313 EXPECT_FALSE(
314 isolated_context()->HandlesFileSystemMountType(kFileSystemTypeTemporary));
315 EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
316 kFileSystemTypePersistent));
317 EXPECT_FALSE(
318 isolated_context()->HandlesFileSystemMountType(kFileSystemTypeTest));
319 // Not even if it's isolated subtype.
320 EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
321 kFileSystemTypeNativeLocal));
322 EXPECT_FALSE(
323 isolated_context()->HandlesFileSystemMountType(kFileSystemTypeDragged));
324 EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
325 kFileSystemTypeNativeMedia));
326 EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
327 kFileSystemTypeDeviceMedia));
328 }
329
TEST_F(IsolatedContextTest,VirtualFileSystemTests)330 TEST_F(IsolatedContextTest, VirtualFileSystemTests) {
331 // Should be able to register empty and non-absolute paths
332 std::string empty_fsid = isolated_context()->RegisterFileSystemForVirtualPath(
333 kFileSystemTypeIsolated, "_", base::FilePath());
334 std::string relative_fsid =
335 isolated_context()->RegisterFileSystemForVirtualPath(
336 kFileSystemTypeIsolated, "_", base::FilePath(FPL("relpath")));
337 ASSERT_FALSE(empty_fsid.empty());
338 ASSERT_FALSE(relative_fsid.empty());
339
340 // Make sure that filesystem root is not prepended to cracked virtual paths.
341 base::FilePath database_root = base::FilePath(DRIVE FPL("/database_path"));
342 std::string database_fsid =
343 isolated_context()->RegisterFileSystemForVirtualPath(
344 kFileSystemTypeIsolated, "_", database_root);
345
346 base::FilePath test_virtual_path =
347 base::FilePath().AppendASCII("virtualdir").AppendASCII("virtualfile.txt");
348
349 base::FilePath whole_virtual_path = isolated_context()
350 ->CreateVirtualRootPath(database_fsid)
351 .AppendASCII("_")
352 .Append(test_virtual_path);
353
354 std::string cracked_id;
355 base::FilePath cracked_path;
356 std::string cracked_inner_id;
357 FileSystemMountOption cracked_option;
358 ASSERT_TRUE(isolated_context()->CrackVirtualPath(
359 whole_virtual_path, &cracked_id, nullptr, &cracked_inner_id,
360 &cracked_path, &cracked_option));
361 ASSERT_EQ(database_fsid, cracked_id);
362 ASSERT_EQ(test_virtual_path, cracked_path);
363 EXPECT_TRUE(cracked_inner_id.empty());
364 }
365
366 } // namespace storage
367