OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/common/fileapi/file_system_util.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/files/file_path.h" | |
10 #include "base/logging.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/strings/sys_string_conversions.h" | |
13 #include "base/strings/utf_string_conversions.h" | |
14 #include "net/base/escape.h" | |
15 #include "net/base/net_errors.h" | |
16 #include "url/gurl.h" | |
17 #include "webkit/common/database/database_identifier.h" | |
18 | |
19 namespace fileapi { | |
20 | |
21 const char kPersistentDir[] = "/persistent"; | |
22 const char kTemporaryDir[] = "/temporary"; | |
23 const char kIsolatedDir[] = "/isolated"; | |
24 const char kExternalDir[] = "/external"; | |
25 const char kTestDir[] = "/test"; | |
26 | |
27 const base::FilePath::CharType VirtualPath::kRoot[] = FILE_PATH_LITERAL("/"); | |
28 const base::FilePath::CharType VirtualPath::kSeparator = FILE_PATH_LITERAL('/'); | |
29 | |
30 // TODO(ericu): Consider removing support for '\', even on Windows, if possible. | |
31 // There's a lot of test code that will need reworking, and we may have trouble | |
32 // with base::FilePath elsewhere [e.g. DirName and other methods may also need | |
33 // replacement]. | |
34 base::FilePath VirtualPath::BaseName(const base::FilePath& virtual_path) { | |
35 base::FilePath::StringType path = virtual_path.value(); | |
36 | |
37 // Keep everything after the final separator, but if the pathname is only | |
38 // one character and it's a separator, leave it alone. | |
39 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) | |
40 path.resize(path.size() - 1); | |
41 base::FilePath::StringType::size_type last_separator = | |
42 path.find_last_of(base::FilePath::kSeparators); | |
43 if (last_separator != base::FilePath::StringType::npos && | |
44 last_separator < path.size() - 1) | |
45 path.erase(0, last_separator + 1); | |
46 | |
47 return base::FilePath(path); | |
48 } | |
49 | |
50 base::FilePath VirtualPath::DirName(const base::FilePath& virtual_path) { | |
51 typedef base::FilePath::StringType StringType; | |
52 StringType path = virtual_path.value(); | |
53 | |
54 // The logic below is taken from that of base::FilePath::DirName, except | |
55 // that this version never cares about '//' or drive-letters even on win32. | |
56 | |
57 // Strip trailing separators. | |
58 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) | |
59 path.resize(path.size() - 1); | |
60 | |
61 StringType::size_type last_separator = | |
62 path.find_last_of(base::FilePath::kSeparators); | |
63 if (last_separator == StringType::npos) { | |
64 // path_ is in the current directory. | |
65 return base::FilePath(base::FilePath::kCurrentDirectory); | |
66 } | |
67 if (last_separator == 0) { | |
68 // path_ is in the root directory. | |
69 return base::FilePath(path.substr(0, 1)); | |
70 } | |
71 // path_ is somewhere else, trim the basename. | |
72 path.resize(last_separator); | |
73 | |
74 // Strip trailing separators. | |
75 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) | |
76 path.resize(path.size() - 1); | |
77 | |
78 if (path.empty()) | |
79 return base::FilePath(base::FilePath::kCurrentDirectory); | |
80 | |
81 return base::FilePath(path); | |
82 } | |
83 | |
84 void VirtualPath::GetComponents( | |
85 const base::FilePath& path, | |
86 std::vector<base::FilePath::StringType>* components) { | |
87 typedef base::FilePath::StringType StringType; | |
88 | |
89 DCHECK(components); | |
90 if (!components) | |
91 return; | |
92 components->clear(); | |
93 if (path.value().empty()) | |
94 return; | |
95 | |
96 StringType::size_type begin = 0, end = 0; | |
97 while (begin < path.value().length() && end != StringType::npos) { | |
98 end = path.value().find_first_of(base::FilePath::kSeparators, begin); | |
99 StringType component = path.value().substr( | |
100 begin, end == StringType::npos ? StringType::npos : end - begin); | |
101 if (!component.empty() && component != base::FilePath::kCurrentDirectory) | |
102 components->push_back(component); | |
103 begin = end + 1; | |
104 } | |
105 } | |
106 | |
107 void VirtualPath::GetComponentsUTF8Unsafe( | |
108 const base::FilePath& path, | |
109 std::vector<std::string>* components) { | |
110 DCHECK(components); | |
111 if (!components) | |
112 return; | |
113 components->clear(); | |
114 | |
115 std::vector<base::FilePath::StringType> stringtype_components; | |
116 VirtualPath::GetComponents(path, &stringtype_components); | |
117 std::vector<base::FilePath::StringType>::const_iterator it; | |
118 for (it = stringtype_components.begin(); it != stringtype_components.end(); | |
119 ++it) { | |
120 components->push_back(base::FilePath(*it).AsUTF8Unsafe()); | |
121 } | |
122 } | |
123 | |
124 base::FilePath::StringType VirtualPath::GetNormalizedFilePath( | |
125 const base::FilePath& path) { | |
126 base::FilePath::StringType normalized_path = path.value(); | |
127 const size_t num_separators = base::FilePath::StringType( | |
128 base::FilePath::kSeparators).length(); | |
129 for (size_t i = 0; i < num_separators; ++i) { | |
130 std::replace(normalized_path.begin(), normalized_path.end(), | |
131 base::FilePath::kSeparators[i], kSeparator); | |
132 } | |
133 | |
134 return (IsAbsolute(normalized_path)) ? | |
135 normalized_path : base::FilePath::StringType(kRoot) + normalized_path; | |
136 } | |
137 | |
138 bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) { | |
139 return path.find(kRoot) == 0; | |
140 } | |
141 | |
142 bool VirtualPath::IsRootPath(const base::FilePath& path) { | |
143 std::vector<base::FilePath::StringType> components; | |
144 VirtualPath::GetComponents(path, &components); | |
145 return (path.empty() || components.empty() || | |
146 (components.size() == 1 && | |
147 components[0] == VirtualPath::kRoot)); | |
148 } | |
149 | |
150 bool ParseFileSystemSchemeURL(const GURL& url, | |
151 GURL* origin_url, | |
152 FileSystemType* type, | |
153 base::FilePath* virtual_path) { | |
154 GURL origin; | |
155 FileSystemType file_system_type = kFileSystemTypeUnknown; | |
156 | |
157 if (!url.is_valid() || !url.SchemeIsFileSystem()) | |
158 return false; | |
159 | |
160 const struct { | |
161 FileSystemType type; | |
162 const char* dir; | |
163 } kValidTypes[] = { | |
164 { kFileSystemTypePersistent, kPersistentDir }, | |
165 { kFileSystemTypeTemporary, kTemporaryDir }, | |
166 { kFileSystemTypeIsolated, kIsolatedDir }, | |
167 { kFileSystemTypeExternal, kExternalDir }, | |
168 { kFileSystemTypeTest, kTestDir }, | |
169 }; | |
170 | |
171 // A path of the inner_url contains only mount type part (e.g. "/temporary"). | |
172 DCHECK(url.inner_url()); | |
173 std::string inner_path = url.inner_url()->path(); | |
174 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidTypes); ++i) { | |
175 if (inner_path == kValidTypes[i].dir) { | |
176 file_system_type = kValidTypes[i].type; | |
177 break; | |
178 } | |
179 } | |
180 | |
181 if (file_system_type == kFileSystemTypeUnknown) | |
182 return false; | |
183 | |
184 std::string path = net::UnescapeURLComponent(url.path(), | |
185 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS | | |
186 net::UnescapeRule::CONTROL_CHARS); | |
187 | |
188 // Ensure the path is relative. | |
189 while (!path.empty() && path[0] == '/') | |
190 path.erase(0, 1); | |
191 | |
192 base::FilePath converted_path = base::FilePath::FromUTF8Unsafe(path); | |
193 | |
194 // All parent references should have been resolved in the renderer. | |
195 if (converted_path.ReferencesParent()) | |
196 return false; | |
197 | |
198 if (origin_url) | |
199 *origin_url = url.GetOrigin(); | |
200 if (type) | |
201 *type = file_system_type; | |
202 if (virtual_path) | |
203 *virtual_path = converted_path.NormalizePathSeparators(). | |
204 StripTrailingSeparators(); | |
205 | |
206 return true; | |
207 } | |
208 | |
209 GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) { | |
210 // origin_url is based on a security origin, so http://foo.com or file:/// | |
211 // instead of the corresponding filesystem URL. | |
212 DCHECK(!origin_url.SchemeIsFileSystem()); | |
213 | |
214 std::string url = "filesystem:" + origin_url.GetWithEmptyPath().spec(); | |
215 switch (type) { | |
216 case kFileSystemTypeTemporary: | |
217 url += (kTemporaryDir + 1); // We don't want the leading slash. | |
218 return GURL(url + "/"); | |
219 case kFileSystemTypePersistent: | |
220 url += (kPersistentDir + 1); // We don't want the leading slash. | |
221 return GURL(url + "/"); | |
222 case kFileSystemTypeExternal: | |
223 url += (kExternalDir + 1); // We don't want the leading slash. | |
224 return GURL(url + "/"); | |
225 case kFileSystemTypeIsolated: | |
226 url += (kIsolatedDir + 1); // We don't want the leading slash. | |
227 return GURL(url + "/"); | |
228 case kFileSystemTypeTest: | |
229 url += (kTestDir + 1); // We don't want the leading slash. | |
230 return GURL(url + "/"); | |
231 // Internal types are always pointed via isolated or external URLs. | |
232 default: | |
233 NOTREACHED(); | |
234 } | |
235 NOTREACHED(); | |
236 return GURL(); | |
237 } | |
238 | |
239 std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) { | |
240 std::string origin_identifier = | |
241 webkit_database::GetIdentifierFromOrigin(origin_url); | |
242 std::string type_string = GetFileSystemTypeString(type); | |
243 DCHECK(!type_string.empty()); | |
244 return origin_identifier + ":" + type_string; | |
245 } | |
246 | |
247 FileSystemType QuotaStorageTypeToFileSystemType( | |
248 quota::StorageType storage_type) { | |
249 switch (storage_type) { | |
250 case quota::kStorageTypeTemporary: | |
251 return kFileSystemTypeTemporary; | |
252 case quota::kStorageTypePersistent: | |
253 return kFileSystemTypePersistent; | |
254 case quota::kStorageTypeSyncable: | |
255 return kFileSystemTypeSyncable; | |
256 case quota::kStorageTypeQuotaNotManaged: | |
257 case quota::kStorageTypeUnknown: | |
258 return kFileSystemTypeUnknown; | |
259 } | |
260 return kFileSystemTypeUnknown; | |
261 } | |
262 | |
263 quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) { | |
264 switch (type) { | |
265 case kFileSystemTypeTemporary: | |
266 return quota::kStorageTypeTemporary; | |
267 case kFileSystemTypePersistent: | |
268 return quota::kStorageTypePersistent; | |
269 case kFileSystemTypeSyncable: | |
270 case kFileSystemTypeSyncableForInternalSync: | |
271 return quota::kStorageTypeSyncable; | |
272 case kFileSystemTypePluginPrivate: | |
273 return quota::kStorageTypeQuotaNotManaged; | |
274 default: | |
275 return quota::kStorageTypeUnknown; | |
276 } | |
277 } | |
278 | |
279 std::string GetFileSystemTypeString(FileSystemType type) { | |
280 switch (type) { | |
281 case kFileSystemTypeTemporary: | |
282 return "Temporary"; | |
283 case kFileSystemTypePersistent: | |
284 return "Persistent"; | |
285 case kFileSystemTypeIsolated: | |
286 return "Isolated"; | |
287 case kFileSystemTypeExternal: | |
288 return "External"; | |
289 case kFileSystemTypeTest: | |
290 return "Test"; | |
291 case kFileSystemTypeNativeLocal: | |
292 return "NativeLocal"; | |
293 case kFileSystemTypeRestrictedNativeLocal: | |
294 return "RestrictedNativeLocal"; | |
295 case kFileSystemTypeDragged: | |
296 return "Dragged"; | |
297 case kFileSystemTypeNativeMedia: | |
298 return "NativeMedia"; | |
299 case kFileSystemTypeDeviceMedia: | |
300 return "DeviceMedia"; | |
301 case kFileSystemTypePicasa: | |
302 return "Picasa"; | |
303 case kFileSystemTypeItunes: | |
304 return "Itunes"; | |
305 case kFileSystemTypeIphoto: | |
306 return "Iphoto"; | |
307 case kFileSystemTypeDrive: | |
308 return "Drive"; | |
309 case kFileSystemTypeSyncable: | |
310 case kFileSystemTypeSyncableForInternalSync: | |
311 return "Syncable"; | |
312 case kFileSystemTypeNativeForPlatformApp: | |
313 return "NativeForPlatformApp"; | |
314 case kFileSystemTypeForTransientFile: | |
315 return "TransientFile"; | |
316 case kFileSystemTypePluginPrivate: | |
317 return "PluginPrivate"; | |
318 case kFileSystemTypeCloudDevice: | |
319 return "CloudDevice"; | |
320 case kFileSystemTypeProvided: | |
321 return "Provided"; | |
322 case kFileSystemTypeDeviceMediaAsFileStorage: | |
323 return "DeviceMediaStorage"; | |
324 case kFileSystemInternalTypeEnumStart: | |
325 case kFileSystemInternalTypeEnumEnd: | |
326 NOTREACHED(); | |
327 // Fall through. | |
328 case kFileSystemTypeUnknown: | |
329 return "Unknown"; | |
330 } | |
331 NOTREACHED(); | |
332 return std::string(); | |
333 } | |
334 | |
335 std::string FilePathToString(const base::FilePath& file_path) { | |
336 #if defined(OS_WIN) | |
337 return base::UTF16ToUTF8(file_path.value()); | |
338 #elif defined(OS_POSIX) | |
339 return file_path.value(); | |
340 #endif | |
341 } | |
342 | |
343 base::FilePath StringToFilePath(const std::string& file_path_string) { | |
344 #if defined(OS_WIN) | |
345 return base::FilePath(base::UTF8ToUTF16(file_path_string)); | |
346 #elif defined(OS_POSIX) | |
347 return base::FilePath(file_path_string); | |
348 #endif | |
349 } | |
350 | |
351 blink::WebFileError FileErrorToWebFileError( | |
352 base::File::Error error_code) { | |
353 switch (error_code) { | |
354 case base::File::FILE_ERROR_NOT_FOUND: | |
355 return blink::WebFileErrorNotFound; | |
356 case base::File::FILE_ERROR_INVALID_OPERATION: | |
357 case base::File::FILE_ERROR_EXISTS: | |
358 case base::File::FILE_ERROR_NOT_EMPTY: | |
359 return blink::WebFileErrorInvalidModification; | |
360 case base::File::FILE_ERROR_NOT_A_DIRECTORY: | |
361 case base::File::FILE_ERROR_NOT_A_FILE: | |
362 return blink::WebFileErrorTypeMismatch; | |
363 case base::File::FILE_ERROR_ACCESS_DENIED: | |
364 return blink::WebFileErrorNoModificationAllowed; | |
365 case base::File::FILE_ERROR_FAILED: | |
366 return blink::WebFileErrorInvalidState; | |
367 case base::File::FILE_ERROR_ABORT: | |
368 return blink::WebFileErrorAbort; | |
369 case base::File::FILE_ERROR_SECURITY: | |
370 return blink::WebFileErrorSecurity; | |
371 case base::File::FILE_ERROR_NO_SPACE: | |
372 return blink::WebFileErrorQuotaExceeded; | |
373 case base::File::FILE_ERROR_INVALID_URL: | |
374 return blink::WebFileErrorEncoding; | |
375 default: | |
376 return blink::WebFileErrorInvalidModification; | |
377 } | |
378 } | |
379 | |
380 bool GetFileSystemPublicType( | |
381 const std::string type_string, | |
382 blink::WebFileSystemType* type) { | |
383 DCHECK(type); | |
384 if (type_string == "Temporary") { | |
385 *type = blink::WebFileSystemTypeTemporary; | |
386 return true; | |
387 } | |
388 if (type_string == "Persistent") { | |
389 *type = blink::WebFileSystemTypePersistent; | |
390 return true; | |
391 } | |
392 if (type_string == "Isolated") { | |
393 *type = blink::WebFileSystemTypeIsolated; | |
394 return true; | |
395 } | |
396 if (type_string == "External") { | |
397 *type = blink::WebFileSystemTypeExternal; | |
398 return true; | |
399 } | |
400 NOTREACHED(); | |
401 return false; | |
402 } | |
403 | |
404 std::string GetIsolatedFileSystemName(const GURL& origin_url, | |
405 const std::string& filesystem_id) { | |
406 std::string name(fileapi::GetFileSystemName( | |
407 origin_url, fileapi::kFileSystemTypeIsolated)); | |
408 name.append("_"); | |
409 name.append(filesystem_id); | |
410 return name; | |
411 } | |
412 | |
413 bool CrackIsolatedFileSystemName(const std::string& filesystem_name, | |
414 std::string* filesystem_id) { | |
415 DCHECK(filesystem_id); | |
416 | |
417 // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}. | |
418 std::string start_token(":"); | |
419 start_token = start_token.append( | |
420 GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_"); | |
421 // WebKit uses different case in its constant for isolated file system | |
422 // names, so we do a case insensitive compare by converting both strings | |
423 // to uppercase. | |
424 // TODO(benwells): Remove this when WebKit uses the same constant. | |
425 start_token = StringToUpperASCII(start_token); | |
426 std::string filesystem_name_upper = StringToUpperASCII(filesystem_name); | |
427 size_t pos = filesystem_name_upper.find(start_token); | |
428 if (pos == std::string::npos) | |
429 return false; | |
430 if (pos == 0) | |
431 return false; | |
432 | |
433 *filesystem_id = filesystem_name.substr(pos + start_token.length(), | |
434 std::string::npos); | |
435 if (filesystem_id->empty()) | |
436 return false; | |
437 | |
438 return true; | |
439 } | |
440 | |
441 bool ValidateIsolatedFileSystemId(const std::string& filesystem_id) { | |
442 const size_t kExpectedFileSystemIdSize = 32; | |
443 if (filesystem_id.size() != kExpectedFileSystemIdSize) | |
444 return false; | |
445 const std::string kExpectedChars("ABCDEF0123456789"); | |
446 return base::ContainsOnlyChars(filesystem_id, kExpectedChars); | |
447 } | |
448 | |
449 std::string GetIsolatedFileSystemRootURIString( | |
450 const GURL& origin_url, | |
451 const std::string& filesystem_id, | |
452 const std::string& optional_root_name) { | |
453 std::string root = GetFileSystemRootURI(origin_url, | |
454 kFileSystemTypeIsolated).spec(); | |
455 if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent()) | |
456 return std::string(); | |
457 root.append(net::EscapePath(filesystem_id)); | |
458 root.append("/"); | |
459 if (!optional_root_name.empty()) { | |
460 if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent()) | |
461 return std::string(); | |
462 root.append(net::EscapePath(optional_root_name)); | |
463 root.append("/"); | |
464 } | |
465 return root; | |
466 } | |
467 | |
468 std::string GetExternalFileSystemRootURIString( | |
469 const GURL& origin_url, | |
470 const std::string& mount_name) { | |
471 std::string root = GetFileSystemRootURI(origin_url, | |
472 kFileSystemTypeExternal).spec(); | |
473 if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent()) | |
474 return std::string(); | |
475 root.append(net::EscapePath(mount_name)); | |
476 root.append("/"); | |
477 return root; | |
478 } | |
479 | |
480 base::File::Error NetErrorToFileError(int error) { | |
481 switch (error) { | |
482 case net::OK: | |
483 return base::File::FILE_OK; | |
484 case net::ERR_ADDRESS_IN_USE: | |
485 return base::File::FILE_ERROR_IN_USE; | |
486 case net::ERR_FILE_EXISTS: | |
487 return base::File::FILE_ERROR_EXISTS; | |
488 case net::ERR_FILE_NOT_FOUND: | |
489 return base::File::FILE_ERROR_NOT_FOUND; | |
490 case net::ERR_ACCESS_DENIED: | |
491 return base::File::FILE_ERROR_ACCESS_DENIED; | |
492 case net::ERR_TOO_MANY_SOCKET_STREAMS: | |
493 return base::File::FILE_ERROR_TOO_MANY_OPENED; | |
494 case net::ERR_OUT_OF_MEMORY: | |
495 return base::File::FILE_ERROR_NO_MEMORY; | |
496 case net::ERR_FILE_NO_SPACE: | |
497 return base::File::FILE_ERROR_NO_SPACE; | |
498 case net::ERR_INVALID_ARGUMENT: | |
499 case net::ERR_INVALID_HANDLE: | |
500 return base::File::FILE_ERROR_INVALID_OPERATION; | |
501 case net::ERR_ABORTED: | |
502 case net::ERR_CONNECTION_ABORTED: | |
503 return base::File::FILE_ERROR_ABORT; | |
504 case net::ERR_ADDRESS_INVALID: | |
505 case net::ERR_INVALID_URL: | |
506 return base::File::FILE_ERROR_INVALID_URL; | |
507 default: | |
508 return base::File::FILE_ERROR_FAILED; | |
509 } | |
510 } | |
511 | |
512 } // namespace fileapi | |
OLD | NEW |