OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/fileapi/file_system_path_manager.h" | 5 #include "webkit/fileapi/file_system_path_manager.h" |
6 | 6 |
7 #include "base/rand_util.h" | 7 #include "base/rand_util.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
11 #include "base/scoped_callback_factory.h" | 11 #include "base/scoped_callback_factory.h" |
12 #include "base/scoped_ptr.h" | 12 #include "base/scoped_ptr.h" |
13 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "googleurl/src/gurl.h" | 16 #include "googleurl/src/gurl.h" |
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystem.h" | 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystem.h" |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | 18 #include "webkit/fileapi/file_system_util.h" |
| 19 #include "webkit/fileapi/sandbox_mount_point_provider.h" |
20 #include "webkit/glue/webkit_glue.h" | 20 #include "webkit/glue/webkit_glue.h" |
21 | 21 |
22 // We use some of WebKit types for conversions between origin identifiers | 22 // We use some of WebKit types for conversions between origin identifiers |
23 // and origin URLs. | 23 // and origin URLs. |
24 using WebKit::WebFileSystem; | 24 using WebKit::WebFileSystem; |
25 using WebKit::WebSecurityOrigin; | |
26 using WebKit::WebString; | |
27 | 25 |
28 using base::PlatformFileError; | 26 using base::PlatformFileError; |
29 | 27 |
30 namespace fileapi { | |
31 | |
32 const FilePath::CharType FileSystemPathManager::kFileSystemDirectory[] = | |
33 FILE_PATH_LITERAL("FileSystem"); | |
34 | |
35 const char FileSystemPathManager::kPersistentName[] = "Persistent"; | |
36 const char FileSystemPathManager::kTemporaryName[] = "Temporary"; | |
37 | |
38 static const FilePath::CharType kFileSystemUniqueNamePrefix[] = | |
39 FILE_PATH_LITERAL("chrome-"); | |
40 static const int kFileSystemUniqueLength = 16; | |
41 static const unsigned kFileSystemUniqueDirectoryNameLength = | |
42 kFileSystemUniqueLength + arraysize(kFileSystemUniqueNamePrefix) - 1; | |
43 | |
44 namespace { | |
45 | |
46 // Restricted names. | |
47 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions | |
48 static const char* const kRestrictedNames[] = { | |
49 "con", "prn", "aux", "nul", | |
50 "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", | |
51 "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", | |
52 }; | |
53 | |
54 // Restricted chars. | |
55 static const FilePath::CharType kRestrictedChars[] = { | |
56 '/', '\\', '<', '>', ':', '?', '*', '"', '|', | |
57 }; | |
58 | |
59 inline std::string FilePathStringToASCII( | |
60 const FilePath::StringType& path_string) { | |
61 #if defined(OS_WIN) | |
62 return WideToASCII(path_string); | |
63 #elif defined(OS_POSIX) | |
64 return path_string; | |
65 #endif | |
66 } | |
67 | |
68 FilePath::StringType CreateUniqueDirectoryName(const GURL& origin_url) { | |
69 // This can be anything but need to be unpredictable. | |
70 static const FilePath::CharType letters[] = FILE_PATH_LITERAL( | |
71 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); | |
72 FilePath::StringType unique(kFileSystemUniqueNamePrefix); | |
73 for (int i = 0; i < kFileSystemUniqueLength; ++i) | |
74 unique += letters[base::RandInt(0, arraysize(letters) - 2)]; | |
75 return unique; | |
76 } | |
77 | |
78 static const char kExtensionScheme[] = "chrome-extension"; | 28 static const char kExtensionScheme[] = "chrome-extension"; |
79 | 29 |
80 } // anonymous namespace | 30 namespace fileapi { |
81 | |
82 class FileSystemPathManager::GetFileSystemRootPathTask | |
83 : public base::RefCountedThreadSafe< | |
84 FileSystemPathManager::GetFileSystemRootPathTask> { | |
85 public: | |
86 GetFileSystemRootPathTask( | |
87 scoped_refptr<base::MessageLoopProxy> file_message_loop, | |
88 const std::string& name, | |
89 FileSystemPathManager::GetRootPathCallback* callback) | |
90 : file_message_loop_(file_message_loop), | |
91 origin_message_loop_proxy_( | |
92 base::MessageLoopProxy::CreateForCurrentThread()), | |
93 name_(name), | |
94 callback_(callback) { | |
95 } | |
96 | |
97 void Start(const GURL& origin_url, | |
98 const FilePath& origin_base_path, | |
99 bool create) { | |
100 file_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | |
101 &GetFileSystemRootPathTask::GetFileSystemRootPathOnFileThread, | |
102 origin_url, origin_base_path, create)); | |
103 } | |
104 | |
105 private: | |
106 void GetFileSystemRootPathOnFileThread( | |
107 const GURL& origin_url, | |
108 const FilePath& base_path, | |
109 bool create) { | |
110 FilePath root; | |
111 if (ReadOriginDirectory(base_path, origin_url, &root)) { | |
112 DispatchCallbackOnCallerThread(root); | |
113 return; | |
114 } | |
115 | |
116 if (!create) { | |
117 DispatchCallbackOnCallerThread(FilePath()); | |
118 return; | |
119 } | |
120 | |
121 // Creates the root directory. | |
122 root = base_path.Append(CreateUniqueDirectoryName(origin_url)); | |
123 if (!file_util::CreateDirectory(root)) { | |
124 DispatchCallbackOnCallerThread(FilePath()); | |
125 return; | |
126 } | |
127 DispatchCallbackOnCallerThread(root); | |
128 } | |
129 | |
130 bool ReadOriginDirectory(const FilePath& base_path, | |
131 const GURL& origin_url, | |
132 FilePath* unique) { | |
133 file_util::FileEnumerator file_enum( | |
134 base_path, false /* recursive */, | |
135 file_util::FileEnumerator::DIRECTORIES, | |
136 FilePath::StringType(kFileSystemUniqueNamePrefix) + | |
137 FILE_PATH_LITERAL("*")); | |
138 FilePath current; | |
139 bool found = false; | |
140 while (!(current = file_enum.Next()).empty()) { | |
141 if (current.BaseName().value().length() != | |
142 kFileSystemUniqueDirectoryNameLength) | |
143 continue; | |
144 if (found) { | |
145 // TODO(kinuko): Should notify the user to ask for some action. | |
146 LOG(WARNING) << "Unexpectedly found more than one FileSystem " | |
147 << "directories for " << origin_url; | |
148 return false; | |
149 } | |
150 found = true; | |
151 *unique = current; | |
152 } | |
153 return !unique->empty(); | |
154 } | |
155 | |
156 void DispatchCallbackOnCallerThread(const FilePath& root_path) { | |
157 origin_message_loop_proxy_->PostTask(FROM_HERE, | |
158 NewRunnableMethod(this, &GetFileSystemRootPathTask::DispatchCallback, | |
159 root_path)); | |
160 } | |
161 | |
162 void DispatchCallback(const FilePath& root_path) { | |
163 callback_->Run(!root_path.empty(), root_path, name_); | |
164 callback_.reset(); | |
165 } | |
166 | |
167 scoped_refptr<base::MessageLoopProxy> file_message_loop_; | |
168 scoped_refptr<base::MessageLoopProxy> origin_message_loop_proxy_; | |
169 std::string name_; | |
170 scoped_ptr<FileSystemPathManager::GetRootPathCallback> callback_; | |
171 }; | |
172 | 31 |
173 FileSystemPathManager::FileSystemPathManager( | 32 FileSystemPathManager::FileSystemPathManager( |
174 scoped_refptr<base::MessageLoopProxy> file_message_loop, | 33 scoped_refptr<base::MessageLoopProxy> file_message_loop, |
175 const FilePath& profile_path, | 34 const FilePath& profile_path, |
176 bool is_incognito, | 35 bool is_incognito, |
177 bool allow_file_access_from_files) | 36 bool allow_file_access_from_files) |
178 : file_message_loop_(file_message_loop), | 37 : is_incognito_(is_incognito), |
179 base_path_(profile_path.Append(kFileSystemDirectory)), | 38 allow_file_access_from_files_(allow_file_access_from_files), |
180 is_incognito_(is_incognito), | 39 sandbox_provider_( |
181 allow_file_access_from_files_(allow_file_access_from_files) { | 40 new SandboxMountPointProvider( |
| 41 ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 42 file_message_loop, |
| 43 profile_path)) { |
182 } | 44 } |
183 | 45 |
184 FileSystemPathManager::~FileSystemPathManager() {} | 46 FileSystemPathManager::~FileSystemPathManager() {} |
185 | 47 |
186 void FileSystemPathManager::GetFileSystemRootPath( | 48 void FileSystemPathManager::GetFileSystemRootPath( |
187 const GURL& origin_url, fileapi::FileSystemType type, | 49 const GURL& origin_url, fileapi::FileSystemType type, |
188 bool create, GetRootPathCallback* callback_ptr) { | 50 bool create, GetRootPathCallback* callback_ptr) { |
189 scoped_ptr<GetRootPathCallback> callback(callback_ptr); | 51 |
190 if (is_incognito_) { | 52 switch (type) { |
191 // TODO(kinuko): return an isolated temporary directory. | 53 case kFileSystemTypeTemporary: |
192 callback->Run(false, FilePath(), std::string()); | 54 case kFileSystemTypePersistent: |
193 return; | 55 sandbox_provider_->GetFileSystemRootPath( |
| 56 origin_url, type, create, callback_ptr); |
| 57 break; |
| 58 case kFileSystemTypeUnknown: |
| 59 default: |
| 60 NOTREACHED(); |
| 61 callback_ptr->Run(false, FilePath(), std::string()); |
194 } | 62 } |
| 63 } |
195 | 64 |
196 if (!IsAllowedScheme(origin_url)) { | 65 FilePath FileSystemPathManager::GetFileSystemRootPathOnFileThread( |
197 callback->Run(false, FilePath(), std::string()); | 66 const GURL& origin_url, FileSystemType type, bool create) { |
198 return; | 67 switch (type) { |
| 68 case kFileSystemTypeTemporary: |
| 69 case kFileSystemTypePersistent: |
| 70 return sandbox_provider_->GetFileSystemRootPathOnFileThread( |
| 71 origin_url, type, create); |
| 72 break; |
| 73 case kFileSystemTypeUnknown: |
| 74 default: |
| 75 NOTREACHED(); |
| 76 return FilePath(); |
199 } | 77 } |
200 | |
201 std::string origin_identifier = GetOriginIdentifierFromURL(origin_url); | |
202 FilePath origin_base_path = GetFileSystemBaseDirectoryForOriginAndType( | |
203 base_path(), origin_identifier, type); | |
204 if (origin_base_path.empty()) { | |
205 callback->Run(false, FilePath(), std::string()); | |
206 return; | |
207 } | |
208 | |
209 std::string type_string = GetFileSystemTypeString(type); | |
210 DCHECK(!type_string.empty()); | |
211 | |
212 scoped_refptr<GetFileSystemRootPathTask> task( | |
213 new GetFileSystemRootPathTask(file_message_loop_, | |
214 origin_identifier + ":" + type_string, | |
215 callback.release())); | |
216 task->Start(origin_url, origin_base_path, create); | |
217 } | 78 } |
218 | 79 |
219 bool FileSystemPathManager::CrackFileSystemPath( | 80 bool FileSystemPathManager::CrackFileSystemPath( |
220 const FilePath& path, GURL* origin_url, FileSystemType* type, | 81 const FilePath& path, GURL* origin_url, FileSystemType* type, |
221 FilePath* virtual_path) const { | 82 FilePath* virtual_path) const { |
222 // Any paths that includes parent references are considered invalid. | 83 // TODO(ericu): |
223 if (path.ReferencesParent()) | 84 // Paths come in here [for now] as a URL, followed by a virtual path in |
| 85 // platform format. For example, on Windows, this will look like |
| 86 // filesystem:http://www.example.com/temporary/\path\to\file.txt. |
| 87 // A potentially dangerous malicious path on Windows might look like: |
| 88 // filesystem:http://www.example.com/temporary/foo/../../\path\to\file.txt. |
| 89 // This code is ugly, but will get cleaned up as we fix the calling side. |
| 90 // Eventually there won't be a distinction between a filesystem path and a |
| 91 // filesystem URL--they'll all be URLs. |
| 92 // We should be passing these to WebKit as string, not FilePath, for ease of |
| 93 // manipulation, or possibly as GURL/KURL. |
| 94 |
| 95 std::string path_as_string; |
| 96 #ifdef OS_WIN |
| 97 path_as_string = WideToUTF8(path.value()); |
| 98 #else |
| 99 path_as_string = path.value(); |
| 100 #endif |
| 101 GURL path_as_url(path_as_string); |
| 102 |
| 103 FilePath local_path; |
| 104 GURL local_url; |
| 105 FileSystemType local_type; |
| 106 if (!CrackFileSystemURL(path_as_url, &local_url, &local_type, &local_path)) |
224 return false; | 107 return false; |
225 | 108 |
226 // The path should be a child of the profile FileSystem path. | 109 #if defined(FILE_PATH_USES_WIN_SEPARATORS) |
227 FilePath relative; | 110 // TODO(ericu): This puts the separators back to windows-standard; they come |
228 if (!base_path_.AppendRelativePath(path, &relative)) | 111 // out of the above code as '/' no matter the platform. Long-term, we'll |
229 return false; | 112 // want to let the underlying FileSystemFileUtil implementation do this part, |
| 113 // since they won't all need it. |
| 114 local_path = local_path.NormalizeWindowsPathSeparators(); |
| 115 #endif |
230 | 116 |
231 // The relative path from the profile FileSystem path should contain | 117 // Any paths that include parent references are considered invalid. |
232 // at least three components, one for storage identifier, one for type | 118 // These should have been taken care of in CrackFileSystemURL. |
233 // and one for the 'unique' part. | 119 DCHECK(!local_path.ReferencesParent()); |
234 std::vector<FilePath::StringType> components; | |
235 relative.GetComponents(&components); | |
236 if (components.size() < 3) | |
237 return false; | |
238 | 120 |
239 // The second component of the relative path to the root directory | 121 // The given |local_path| seems valid. Populates the |origin_url|, |type| |
240 // must be kPersistent or kTemporary. | |
241 if (!IsStringASCII(components[1])) | |
242 return false; | |
243 | |
244 std::string ascii_type_component = FilePathStringToASCII(components[1]); | |
245 FileSystemType cracked_type = kFileSystemTypeUnknown; | |
246 if (ascii_type_component == kPersistentName) | |
247 cracked_type = kFileSystemTypePersistent; | |
248 else if (ascii_type_component == kTemporaryName) | |
249 cracked_type = kFileSystemTypeTemporary; | |
250 else | |
251 return false; | |
252 | |
253 DCHECK(cracked_type != kFileSystemTypeUnknown); | |
254 | |
255 // The given |path| seems valid. Populates the |origin_url|, |type| | |
256 // and |virtual_path| if they are given. | 122 // and |virtual_path| if they are given. |
257 | 123 |
258 if (origin_url) { | 124 if (origin_url) { |
259 WebSecurityOrigin web_security_origin = | 125 *origin_url = local_url; |
260 WebSecurityOrigin::createFromDatabaseIdentifier( | |
261 webkit_glue::FilePathStringToWebString(components[0])); | |
262 *origin_url = GURL(web_security_origin.toString()); | |
263 | |
264 // We need this work-around for file:/// URIs as | |
265 // createFromDatabaseIdentifier returns empty origin_url for them. | |
266 if (allow_file_access_from_files_ && origin_url->spec().empty() && | |
267 components[0].find(FILE_PATH_LITERAL("file")) == 0) | |
268 *origin_url = GURL("file:///"); | |
269 } | 126 } |
270 | 127 |
271 if (type) | 128 if (type) |
272 *type = cracked_type; | 129 *type = local_type; |
273 | 130 |
274 if (virtual_path) { | 131 if (virtual_path) { |
275 virtual_path->clear(); | 132 *virtual_path = local_path; |
276 for (size_t i = 3; i < components.size(); ++i) | |
277 *virtual_path = virtual_path->Append(components[i]); | |
278 } | 133 } |
279 | 134 |
280 return true; | 135 return true; |
281 } | 136 } |
282 | 137 |
283 bool FileSystemPathManager::IsAllowedScheme(const GURL& url) const { | 138 bool FileSystemPathManager::IsAllowedScheme(const GURL& url) const { |
284 // Basically we only accept http or https. We allow file:// URLs | 139 // Basically we only accept http or https. We allow file:// URLs |
285 // only if --allow-file-access-from-files flag is given. | 140 // only if --allow-file-access-from-files flag is given. |
286 return url.SchemeIs("http") || url.SchemeIs("https") || | 141 return url.SchemeIs("http") || url.SchemeIs("https") || |
287 url.SchemeIs(kExtensionScheme) || | 142 url.SchemeIs(kExtensionScheme) || |
288 (url.SchemeIsFile() && allow_file_access_from_files_); | 143 (url.SchemeIsFile() && allow_file_access_from_files_); |
289 } | 144 } |
290 | 145 |
291 // static | 146 // static |
292 bool FileSystemPathManager::IsRestrictedFileName(const FilePath& filename) { | |
293 if (filename.value().empty()) | |
294 return false; | |
295 | |
296 if (IsWhitespace(filename.value()[filename.value().size() - 1]) || | |
297 filename.value()[filename.value().size() - 1] == '.') | |
298 return true; | |
299 | |
300 std::string filename_lower = StringToLowerASCII( | |
301 FilePathStringToASCII(filename.value())); | |
302 | |
303 for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { | |
304 // Exact match. | |
305 if (filename_lower == kRestrictedNames[i]) | |
306 return true; | |
307 // Starts with "RESTRICTED_NAME.". | |
308 if (filename_lower.find(std::string(kRestrictedNames[i]) + ".") == 0) | |
309 return true; | |
310 } | |
311 | |
312 for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { | |
313 if (filename.value().find(kRestrictedChars[i]) != | |
314 FilePath::StringType::npos) | |
315 return true; | |
316 } | |
317 | |
318 return false; | |
319 } | |
320 | |
321 // static | |
322 std::string FileSystemPathManager::GetFileSystemTypeString( | 147 std::string FileSystemPathManager::GetFileSystemTypeString( |
323 fileapi::FileSystemType type) { | 148 fileapi::FileSystemType type) { |
324 if (type == fileapi::kFileSystemTypeTemporary) | 149 if (type == fileapi::kFileSystemTypeTemporary) |
325 return fileapi::FileSystemPathManager::kTemporaryName; | 150 return fileapi::SandboxMountPointProvider::kTemporaryName; |
326 else if (type == fileapi::kFileSystemTypePersistent) | 151 else if (type == fileapi::kFileSystemTypePersistent) |
327 return fileapi::FileSystemPathManager::kPersistentName; | 152 return fileapi::SandboxMountPointProvider::kPersistentName; |
328 return std::string(); | 153 return std::string(); |
329 } | 154 } |
330 | 155 |
331 // static | 156 // Checks if a given |name| contains any restricted names/chars in it. |
332 std::string FileSystemPathManager::GetOriginIdentifierFromURL( | 157 bool FileSystemPathManager::IsRestrictedFileName( |
333 const GURL& url) { | 158 FileSystemType type, const FilePath& filename) { |
334 WebKit::WebSecurityOrigin web_security_origin = | 159 switch (type) { |
335 WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); | 160 case kFileSystemTypeTemporary: |
336 return web_security_origin.databaseIdentifier().utf8(); | 161 case kFileSystemTypePersistent: |
337 } | 162 return sandbox_provider_->IsRestrictedFileName(filename); |
338 | 163 case kFileSystemTypeUnknown: |
339 // static | 164 default: |
340 FilePath FileSystemPathManager::GetFileSystemBaseDirectoryForOriginAndType( | 165 NOTREACHED(); |
341 const FilePath& base_path, const std::string& origin_identifier, | 166 return true; |
342 fileapi::FileSystemType type) { | |
343 if (origin_identifier.empty()) | |
344 return FilePath(); | |
345 std::string type_string = GetFileSystemTypeString(type); | |
346 if (type_string.empty()) { | |
347 LOG(WARNING) << "Unknown filesystem type is requested:" << type; | |
348 return FilePath(); | |
349 } | 167 } |
350 return base_path.AppendASCII(origin_identifier) | |
351 .AppendASCII(type_string); | |
352 } | |
353 | |
354 FileSystemPathManager::OriginEnumerator::OriginEnumerator( | |
355 const FilePath& base_path) | |
356 : enumerator_(base_path, false /* recursive */, | |
357 file_util::FileEnumerator::DIRECTORIES) { | |
358 } | |
359 | |
360 std::string FileSystemPathManager::OriginEnumerator::Next() { | |
361 current_ = enumerator_.Next(); | |
362 return FilePathStringToASCII(current_.BaseName().value()); | |
363 } | |
364 | |
365 bool FileSystemPathManager::OriginEnumerator::HasTemporary() { | |
366 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( | |
367 FileSystemPathManager::kTemporaryName)); | |
368 } | |
369 | |
370 bool FileSystemPathManager::OriginEnumerator::HasPersistent() { | |
371 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( | |
372 FileSystemPathManager::kPersistentName)); | |
373 } | 168 } |
374 | 169 |
375 } // namespace fileapi | 170 } // namespace fileapi |
376 | 171 |
377 COMPILE_ASSERT(int(WebFileSystem::TypeTemporary) == \ | 172 COMPILE_ASSERT(int(WebFileSystem::TypeTemporary) == \ |
378 int(fileapi::kFileSystemTypeTemporary), mismatching_enums); | 173 int(fileapi::kFileSystemTypeTemporary), mismatching_enums); |
379 COMPILE_ASSERT(int(WebFileSystem::TypePersistent) == \ | 174 COMPILE_ASSERT(int(WebFileSystem::TypePersistent) == \ |
380 int(fileapi::kFileSystemTypePersistent), mismatching_enums); | 175 int(fileapi::kFileSystemTypePersistent), mismatching_enums); |
OLD | NEW |