OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "webkit/fileapi/file_system_path_manager.h" | |
6 | |
7 #include <set> | |
8 #include <string> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/bind.h" | |
12 #include "base/file_util.h" | |
13 #include "base/memory/ref_counted.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/memory/weak_ptr.h" | |
16 #include "base/message_loop.h" | |
17 #include "base/message_loop_proxy.h" | |
18 #include "base/scoped_temp_dir.h" | |
19 #include "base/sys_string_conversions.h" | |
20 #include "base/utf_string_conversions.h" | |
21 #include "googleurl/src/gurl.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 #include "webkit/fileapi/file_system_util.h" | |
24 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
25 #include "webkit/quota/mock_special_storage_policy.h" | |
26 | |
27 namespace fileapi { | |
28 namespace { | |
29 | |
30 // PS stands for path separator. | |
31 #if defined(FILE_PATH_USES_WIN_SEPARATORS) | |
32 #define PS "\\" | |
33 #else | |
34 #define PS "/" | |
35 #endif | |
36 | |
37 struct RootPathTestCase { | |
38 fileapi::FileSystemType type; | |
39 const char* origin_url; | |
40 const char* expected_path; | |
41 }; | |
42 | |
43 const struct RootPathTest { | |
44 fileapi::FileSystemType type; | |
45 const char* origin_url; | |
46 const char* expected_path; | |
47 } kRootPathTestCases[] = { | |
48 { fileapi::kFileSystemTypeTemporary, "http://foo:1/", | |
49 "000" PS "t" }, | |
50 { fileapi::kFileSystemTypePersistent, "http://foo:1/", | |
51 "000" PS "p" }, | |
52 { fileapi::kFileSystemTypeTemporary, "http://bar.com/", | |
53 "001" PS "t" }, | |
54 { fileapi::kFileSystemTypePersistent, "http://bar.com/", | |
55 "001" PS "p" }, | |
56 { fileapi::kFileSystemTypeTemporary, "https://foo:2/", | |
57 "002" PS "t" }, | |
58 { fileapi::kFileSystemTypePersistent, "https://foo:2/", | |
59 "002" PS "p" }, | |
60 { fileapi::kFileSystemTypeTemporary, "https://bar.com/", | |
61 "003" PS "t" }, | |
62 { fileapi::kFileSystemTypePersistent, "https://bar.com/", | |
63 "003" PS "p" }, | |
64 #if defined(OS_CHROMEOS) | |
65 { fileapi::kFileSystemTypeExternal, "chrome-extension://foo/", | |
66 "chrome-extension__0" PS "External" }, | |
67 #endif | |
68 }; | |
69 | |
70 const struct RootPathFileURITest { | |
71 fileapi::FileSystemType type; | |
72 const char* origin_url; | |
73 const char* expected_path; | |
74 const char* virtual_path; | |
75 } kRootPathFileURITestCases[] = { | |
76 { fileapi::kFileSystemTypeTemporary, "file:///", | |
77 "000" PS "t", NULL }, | |
78 { fileapi::kFileSystemTypePersistent, "file:///", | |
79 "000" PS "p", NULL }, | |
80 #if defined(OS_CHROMEOS) | |
81 { fileapi::kFileSystemTypeExternal, "chrome-extension://foo/", | |
82 "chrome-extension__0" PS "External", "testing" }, | |
83 #endif | |
84 }; | |
85 | |
86 const struct CheckValidPathTest { | |
87 FilePath::StringType path; | |
88 bool expected_valid; | |
89 } kCheckValidPathTestCases[] = { | |
90 { FILE_PATH_LITERAL("//tmp/foo.txt"), false, }, | |
91 { FILE_PATH_LITERAL("//etc/hosts"), false, }, | |
92 { FILE_PATH_LITERAL("foo.txt"), true, }, | |
93 { FILE_PATH_LITERAL("a/b/c"), true, }, | |
94 // Any paths that includes parent references are considered invalid. | |
95 { FILE_PATH_LITERAL(".."), false, }, | |
96 { FILE_PATH_LITERAL("tmp/.."), false, }, | |
97 { FILE_PATH_LITERAL("a/b/../c/.."), false, }, | |
98 }; | |
99 | |
100 const char* const kPathToVirtualPathTestCases[] = { | |
101 "", | |
102 "a", | |
103 "a" PS "b", | |
104 "a" PS "b" PS "c", | |
105 }; | |
106 | |
107 const struct IsRestrictedNameTest { | |
108 FilePath::StringType name; | |
109 bool expected_dangerous; | |
110 } kIsRestrictedNameTestCases[] = { | |
111 | |
112 // Names that contain strings that used to be restricted, but are now allowed. | |
113 { FILE_PATH_LITERAL("con"), false, }, | |
114 { FILE_PATH_LITERAL("Con.txt"), false, }, | |
115 { FILE_PATH_LITERAL("Prn.png"), false, }, | |
116 { FILE_PATH_LITERAL("AUX"), false, }, | |
117 { FILE_PATH_LITERAL("nUl."), false, }, | |
118 { FILE_PATH_LITERAL("coM1"), false, }, | |
119 { FILE_PATH_LITERAL("COM3.com"), false, }, | |
120 { FILE_PATH_LITERAL("cOM7"), false, }, | |
121 { FILE_PATH_LITERAL("com9"), false, }, | |
122 { FILE_PATH_LITERAL("lpT1"), false, }, | |
123 { FILE_PATH_LITERAL("LPT4.com"), false, }, | |
124 { FILE_PATH_LITERAL("lPT8"), false, }, | |
125 { FILE_PATH_LITERAL("lPT9"), false, }, | |
126 { FILE_PATH_LITERAL("com1."), false, }, | |
127 | |
128 // Similar cases that have always been allowed. | |
129 { FILE_PATH_LITERAL("con3"), false, }, | |
130 { FILE_PATH_LITERAL("PrnImage.png"), false, }, | |
131 { FILE_PATH_LITERAL("AUXX"), false, }, | |
132 { FILE_PATH_LITERAL("NULL"), false, }, | |
133 { FILE_PATH_LITERAL("coM0"), false, }, | |
134 { FILE_PATH_LITERAL("COM.com"), false, }, | |
135 { FILE_PATH_LITERAL("lpT0"), false, }, | |
136 { FILE_PATH_LITERAL("LPT.com"), false, }, | |
137 | |
138 // Ends with period or whitespace--used to be banned, now OK. | |
139 { FILE_PATH_LITERAL("b "), false, }, | |
140 { FILE_PATH_LITERAL("b\t"), false, }, | |
141 { FILE_PATH_LITERAL("b\n"), false, }, | |
142 { FILE_PATH_LITERAL("b\r\n"), false, }, | |
143 { FILE_PATH_LITERAL("b."), false, }, | |
144 { FILE_PATH_LITERAL("b.."), false, }, | |
145 | |
146 // Similar cases that have always been allowed. | |
147 { FILE_PATH_LITERAL("b c"), false, }, | |
148 { FILE_PATH_LITERAL("b\tc"), false, }, | |
149 { FILE_PATH_LITERAL("b\nc"), false, }, | |
150 { FILE_PATH_LITERAL("b\r\nc"), false, }, | |
151 { FILE_PATH_LITERAL("b c d e f"), false, }, | |
152 { FILE_PATH_LITERAL("b.c"), false, }, | |
153 { FILE_PATH_LITERAL("b..c"), false, }, | |
154 | |
155 // Name that has restricted chars in it. | |
156 { FILE_PATH_LITERAL("\\"), true, }, | |
157 { FILE_PATH_LITERAL("/"), true, }, | |
158 { FILE_PATH_LITERAL("a\\b"), true, }, | |
159 { FILE_PATH_LITERAL("a/b"), true, }, | |
160 { FILE_PATH_LITERAL("ab\\"), true, }, | |
161 { FILE_PATH_LITERAL("ab/"), true, }, | |
162 { FILE_PATH_LITERAL("\\ab"), true, }, | |
163 { FILE_PATH_LITERAL("/ab"), true, }, | |
164 { FILE_PATH_LITERAL("ab/.txt"), true, }, | |
165 { FILE_PATH_LITERAL("ab\\.txt"), true, }, | |
166 | |
167 // Names that contain chars that were formerly restricted, now OK. | |
168 { FILE_PATH_LITERAL("a<b"), false, }, | |
169 { FILE_PATH_LITERAL("a>b"), false, }, | |
170 { FILE_PATH_LITERAL("a:b"), false, }, | |
171 { FILE_PATH_LITERAL("a?b"), false, }, | |
172 { FILE_PATH_LITERAL("a|b"), false, }, | |
173 { FILE_PATH_LITERAL("ab<.txt"), false, }, | |
174 { FILE_PATH_LITERAL("ab>.txt"), false, }, | |
175 { FILE_PATH_LITERAL("ab:.txt"), false, }, | |
176 { FILE_PATH_LITERAL("ab?.txt"), false, }, | |
177 { FILE_PATH_LITERAL("ab|.txt"), false, }, | |
178 { FILE_PATH_LITERAL("<ab"), false, }, | |
179 { FILE_PATH_LITERAL(">ab"), false, }, | |
180 { FILE_PATH_LITERAL(":ab"), false, }, | |
181 { FILE_PATH_LITERAL("?ab"), false, }, | |
182 { FILE_PATH_LITERAL("|ab"), false, }, | |
183 | |
184 // Names that are restricted still. | |
185 { FILE_PATH_LITERAL(".."), true, }, | |
186 { FILE_PATH_LITERAL("."), true, }, | |
187 | |
188 // Similar but safe cases. | |
189 { FILE_PATH_LITERAL(" ."), false, }, | |
190 { FILE_PATH_LITERAL(". "), false, }, | |
191 { FILE_PATH_LITERAL(" . "), false, }, | |
192 { FILE_PATH_LITERAL(" .."), false, }, | |
193 { FILE_PATH_LITERAL(".. "), false, }, | |
194 { FILE_PATH_LITERAL(" .. "), false, }, | |
195 { FILE_PATH_LITERAL("b."), false, }, | |
196 { FILE_PATH_LITERAL(".b"), false, }, | |
197 }; | |
198 | |
199 FilePath UTF8ToFilePath(const std::string& str) { | |
200 FilePath::StringType result; | |
201 #if defined(OS_POSIX) | |
202 result = base::SysWideToNativeMB(UTF8ToWide(str)); | |
203 #elif defined(OS_WIN) | |
204 result = UTF8ToUTF16(str); | |
205 #endif | |
206 return FilePath(result); | |
207 } | |
208 | |
209 } // namespace | |
210 | |
211 class FileSystemPathManagerTest : public testing::Test { | |
212 public: | |
213 FileSystemPathManagerTest() | |
214 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
215 } | |
216 | |
217 void SetUp() { | |
218 ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); | |
219 root_path_callback_status_ = false; | |
220 root_path_.clear(); | |
221 file_system_name_.clear(); | |
222 } | |
223 | |
224 protected: | |
225 FileSystemPathManager* NewPathManager( | |
226 bool incognito, | |
227 bool allow_file_access) { | |
228 FileSystemPathManager* manager = new FileSystemPathManager( | |
229 base::MessageLoopProxy::current(), | |
230 data_dir_.path(), | |
231 scoped_refptr<quota::SpecialStoragePolicy>( | |
232 new quota::MockSpecialStoragePolicy), | |
233 incognito, | |
234 allow_file_access); | |
235 #if defined(OS_CHROMEOS) | |
236 fileapi::ExternalFileSystemMountPointProvider* ext_provider = | |
237 manager->external_provider(); | |
238 ext_provider->AddMountPoint(FilePath("/tmp/testing")); | |
239 #endif | |
240 return manager; | |
241 } | |
242 | |
243 void OnGetRootPath(bool success, | |
244 const FilePath& root_path, | |
245 const std::string& name) { | |
246 root_path_callback_status_ = success; | |
247 root_path_ = root_path; | |
248 file_system_name_ = name; | |
249 } | |
250 | |
251 bool GetRootPath(FileSystemPathManager* manager, | |
252 const GURL& origin_url, | |
253 fileapi::FileSystemType type, | |
254 bool create, | |
255 FilePath* root_path) { | |
256 manager->ValidateFileSystemRootAndGetURL( | |
257 origin_url, type, create, | |
258 base::Bind(&FileSystemPathManagerTest::OnGetRootPath, | |
259 weak_factory_.GetWeakPtr())); | |
260 MessageLoop::current()->RunAllPending(); | |
261 if (root_path) | |
262 *root_path = root_path_; | |
263 return root_path_callback_status_; | |
264 } | |
265 | |
266 FilePath data_path() { return data_dir_.path(); } | |
267 FilePath file_system_path() { | |
268 return data_dir_.path().Append( | |
269 SandboxMountPointProvider::kNewFileSystemDirectory); | |
270 } | |
271 FilePath external_file_system_path() { | |
272 return UTF8ToFilePath(std::string(fileapi::kExternalDir)); | |
273 } | |
274 FilePath external_file_path_root() { | |
275 return UTF8ToFilePath(std::string("/tmp")); | |
276 } | |
277 | |
278 private: | |
279 ScopedTempDir data_dir_; | |
280 base::WeakPtrFactory<FileSystemPathManagerTest> weak_factory_; | |
281 | |
282 bool root_path_callback_status_; | |
283 FilePath root_path_; | |
284 std::string file_system_name_; | |
285 | |
286 DISALLOW_COPY_AND_ASSIGN(FileSystemPathManagerTest); | |
287 }; | |
288 | |
289 TEST_F(FileSystemPathManagerTest, GetRootPathCreateAndExamine) { | |
290 std::vector<FilePath> returned_root_path( | |
291 ARRAYSIZE_UNSAFE(kRootPathTestCases)); | |
292 scoped_ptr<FileSystemPathManager> manager(NewPathManager(false, false)); | |
293 | |
294 // Create a new root directory. | |
295 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { | |
296 SCOPED_TRACE(testing::Message() << "RootPath (create) #" << i << " " | |
297 << kRootPathTestCases[i].expected_path); | |
298 | |
299 FilePath root_path; | |
300 EXPECT_TRUE(GetRootPath(manager.get(), | |
301 GURL(kRootPathTestCases[i].origin_url), | |
302 kRootPathTestCases[i].type, | |
303 true /* create */, &root_path)); | |
304 | |
305 if (kRootPathTestCases[i].type != fileapi::kFileSystemTypeExternal) { | |
306 FilePath expected = file_system_path().AppendASCII( | |
307 kRootPathTestCases[i].expected_path); | |
308 EXPECT_EQ(expected.value(), root_path.value()); | |
309 EXPECT_TRUE(file_util::DirectoryExists(root_path)); | |
310 } else { | |
311 // External file system root path is virtual one and does not match | |
312 // anything from the actual file system. | |
313 EXPECT_EQ(external_file_system_path().value(), | |
314 root_path.value()); | |
315 } | |
316 ASSERT_TRUE(returned_root_path.size() > i); | |
317 returned_root_path[i] = root_path; | |
318 } | |
319 | |
320 // Get the root directory with create=false and see if we get the | |
321 // same directory. | |
322 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { | |
323 SCOPED_TRACE(testing::Message() << "RootPath (get) #" << i << " " | |
324 << kRootPathTestCases[i].expected_path); | |
325 | |
326 FilePath root_path; | |
327 EXPECT_TRUE(GetRootPath(manager.get(), | |
328 GURL(kRootPathTestCases[i].origin_url), | |
329 kRootPathTestCases[i].type, | |
330 false /* create */, &root_path)); | |
331 ASSERT_TRUE(returned_root_path.size() > i); | |
332 EXPECT_EQ(returned_root_path[i].value(), root_path.value()); | |
333 } | |
334 } | |
335 | |
336 TEST_F(FileSystemPathManagerTest, GetRootPathCreateAndExamineWithNewManager) { | |
337 std::vector<FilePath> returned_root_path( | |
338 ARRAYSIZE_UNSAFE(kRootPathTestCases)); | |
339 scoped_ptr<FileSystemPathManager> manager(NewPathManager(false, false)); | |
340 | |
341 GURL origin_url("http://foo.com:1/"); | |
342 | |
343 FilePath root_path1; | |
344 EXPECT_TRUE(GetRootPath(manager.get(), origin_url, | |
345 kFileSystemTypeTemporary, true, &root_path1)); | |
346 | |
347 manager.reset(NewPathManager(false, false)); | |
348 FilePath root_path2; | |
349 EXPECT_TRUE(GetRootPath(manager.get(), origin_url, | |
350 kFileSystemTypeTemporary, false, &root_path2)); | |
351 | |
352 EXPECT_EQ(root_path1.value(), root_path2.value()); | |
353 } | |
354 | |
355 TEST_F(FileSystemPathManagerTest, GetRootPathGetWithoutCreate) { | |
356 scoped_ptr<FileSystemPathManager> manager(NewPathManager(false, false)); | |
357 | |
358 // Try to get a root directory without creating. | |
359 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { | |
360 SCOPED_TRACE(testing::Message() << "RootPath (create=false) #" << i << " " | |
361 << kRootPathTestCases[i].expected_path); | |
362 EXPECT_FALSE(GetRootPath(manager.get(), | |
363 GURL(kRootPathTestCases[i].origin_url), | |
364 kRootPathTestCases[i].type, | |
365 false /* create */, NULL)); | |
366 } | |
367 } | |
368 | |
369 TEST_F(FileSystemPathManagerTest, GetRootPathInIncognito) { | |
370 scoped_ptr<FileSystemPathManager> manager(NewPathManager( | |
371 true /* incognito */, false)); | |
372 | |
373 // Try to get a root directory. | |
374 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { | |
375 SCOPED_TRACE(testing::Message() << "RootPath (incognito) #" << i << " " | |
376 << kRootPathTestCases[i].expected_path); | |
377 EXPECT_FALSE(GetRootPath(manager.get(), | |
378 GURL(kRootPathTestCases[i].origin_url), | |
379 kRootPathTestCases[i].type, | |
380 true /* create */, NULL)); | |
381 } | |
382 } | |
383 | |
384 TEST_F(FileSystemPathManagerTest, GetRootPathFileURI) { | |
385 scoped_ptr<FileSystemPathManager> manager(NewPathManager(false, false)); | |
386 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) { | |
387 SCOPED_TRACE(testing::Message() << "RootPathFileURI (disallow) #" | |
388 << i << " " << kRootPathFileURITestCases[i].expected_path); | |
389 EXPECT_FALSE(GetRootPath(manager.get(), | |
390 GURL(kRootPathFileURITestCases[i].origin_url), | |
391 kRootPathFileURITestCases[i].type, | |
392 true /* create */, NULL)); | |
393 } | |
394 } | |
395 | |
396 TEST_F(FileSystemPathManagerTest, GetRootPathFileURIWithAllowFlag) { | |
397 scoped_ptr<FileSystemPathManager> manager(NewPathManager( | |
398 false, true /* allow_file_access_from_files */)); | |
399 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) { | |
400 SCOPED_TRACE(testing::Message() << "RootPathFileURI (allow) #" | |
401 << i << " " << kRootPathFileURITestCases[i].expected_path); | |
402 FilePath root_path; | |
403 EXPECT_TRUE(GetRootPath(manager.get(), | |
404 GURL(kRootPathFileURITestCases[i].origin_url), | |
405 kRootPathFileURITestCases[i].type, | |
406 true /* create */, &root_path)); | |
407 if (kRootPathFileURITestCases[i].type != fileapi::kFileSystemTypeExternal) { | |
408 FilePath expected = file_system_path().AppendASCII( | |
409 kRootPathFileURITestCases[i].expected_path); | |
410 EXPECT_EQ(expected.value(), root_path.value()); | |
411 EXPECT_TRUE(file_util::DirectoryExists(root_path)); | |
412 } else { | |
413 EXPECT_EQ(external_file_path_root().value(), root_path.value()); | |
414 } | |
415 } | |
416 } | |
417 | |
418 TEST_F(FileSystemPathManagerTest, IsRestrictedName) { | |
419 scoped_ptr<FileSystemPathManager> manager(NewPathManager(false, false)); | |
420 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kIsRestrictedNameTestCases); ++i) { | |
421 SCOPED_TRACE(testing::Message() << "IsRestrictedName #" << i << " " | |
422 << kIsRestrictedNameTestCases[i].name); | |
423 FilePath name(kIsRestrictedNameTestCases[i].name); | |
424 EXPECT_EQ(kIsRestrictedNameTestCases[i].expected_dangerous, | |
425 manager->IsRestrictedFileName(kFileSystemTypeTemporary, name)); | |
426 } | |
427 } | |
428 | |
429 } // namespace fileapi | |
OLD | NEW |