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 storage { | |
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 = storage::GetIdentifierFromOrigin(origin_url); | |
241 std::string type_string = GetFileSystemTypeString(type); | |
242 DCHECK(!type_string.empty()); | |
243 return origin_identifier + ":" + type_string; | |
244 } | |
245 | |
246 FileSystemType QuotaStorageTypeToFileSystemType( | |
247 storage::StorageType storage_type) { | |
248 switch (storage_type) { | |
249 case storage::kStorageTypeTemporary: | |
250 return kFileSystemTypeTemporary; | |
251 case storage::kStorageTypePersistent: | |
252 return kFileSystemTypePersistent; | |
253 case storage::kStorageTypeSyncable: | |
254 return kFileSystemTypeSyncable; | |
255 case storage::kStorageTypeQuotaNotManaged: | |
256 case storage::kStorageTypeUnknown: | |
257 return kFileSystemTypeUnknown; | |
258 } | |
259 return kFileSystemTypeUnknown; | |
260 } | |
261 | |
262 storage::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) { | |
263 switch (type) { | |
264 case kFileSystemTypeTemporary: | |
265 return storage::kStorageTypeTemporary; | |
266 case kFileSystemTypePersistent: | |
267 return storage::kStorageTypePersistent; | |
268 case kFileSystemTypeSyncable: | |
269 case kFileSystemTypeSyncableForInternalSync: | |
270 return storage::kStorageTypeSyncable; | |
271 case kFileSystemTypePluginPrivate: | |
272 return storage::kStorageTypeQuotaNotManaged; | |
273 default: | |
274 return storage::kStorageTypeUnknown; | |
275 } | |
276 } | |
277 | |
278 std::string GetFileSystemTypeString(FileSystemType type) { | |
279 switch (type) { | |
280 case kFileSystemTypeTemporary: | |
281 return "Temporary"; | |
282 case kFileSystemTypePersistent: | |
283 return "Persistent"; | |
284 case kFileSystemTypeIsolated: | |
285 return "Isolated"; | |
286 case kFileSystemTypeExternal: | |
287 return "External"; | |
288 case kFileSystemTypeTest: | |
289 return "Test"; | |
290 case kFileSystemTypeNativeLocal: | |
291 return "NativeLocal"; | |
292 case kFileSystemTypeRestrictedNativeLocal: | |
293 return "RestrictedNativeLocal"; | |
294 case kFileSystemTypeDragged: | |
295 return "Dragged"; | |
296 case kFileSystemTypeNativeMedia: | |
297 return "NativeMedia"; | |
298 case kFileSystemTypeDeviceMedia: | |
299 return "DeviceMedia"; | |
300 case kFileSystemTypePicasa: | |
301 return "Picasa"; | |
302 case kFileSystemTypeItunes: | |
303 return "Itunes"; | |
304 case kFileSystemTypeIphoto: | |
305 return "Iphoto"; | |
306 case kFileSystemTypeDrive: | |
307 return "Drive"; | |
308 case kFileSystemTypeSyncable: | |
309 case kFileSystemTypeSyncableForInternalSync: | |
310 return "Syncable"; | |
311 case kFileSystemTypeNativeForPlatformApp: | |
312 return "NativeForPlatformApp"; | |
313 case kFileSystemTypeForTransientFile: | |
314 return "TransientFile"; | |
315 case kFileSystemTypePluginPrivate: | |
316 return "PluginPrivate"; | |
317 case kFileSystemTypeCloudDevice: | |
318 return "CloudDevice"; | |
319 case kFileSystemTypeProvided: | |
320 return "Provided"; | |
321 case kFileSystemTypeDeviceMediaAsFileStorage: | |
322 return "DeviceMediaStorage"; | |
323 case kFileSystemInternalTypeEnumStart: | |
324 case kFileSystemInternalTypeEnumEnd: | |
325 NOTREACHED(); | |
326 // Fall through. | |
327 case kFileSystemTypeUnknown: | |
328 return "Unknown"; | |
329 } | |
330 NOTREACHED(); | |
331 return std::string(); | |
332 } | |
333 | |
334 std::string FilePathToString(const base::FilePath& file_path) { | |
335 #if defined(OS_WIN) | |
336 return base::UTF16ToUTF8(file_path.value()); | |
337 #elif defined(OS_POSIX) | |
338 return file_path.value(); | |
339 #endif | |
340 } | |
341 | |
342 base::FilePath StringToFilePath(const std::string& file_path_string) { | |
343 #if defined(OS_WIN) | |
344 return base::FilePath(base::UTF8ToUTF16(file_path_string)); | |
345 #elif defined(OS_POSIX) | |
346 return base::FilePath(file_path_string); | |
347 #endif | |
348 } | |
349 | |
350 blink::WebFileError FileErrorToWebFileError( | |
351 base::File::Error error_code) { | |
352 switch (error_code) { | |
353 case base::File::FILE_ERROR_NOT_FOUND: | |
354 return blink::WebFileErrorNotFound; | |
355 case base::File::FILE_ERROR_INVALID_OPERATION: | |
356 case base::File::FILE_ERROR_EXISTS: | |
357 case base::File::FILE_ERROR_NOT_EMPTY: | |
358 return blink::WebFileErrorInvalidModification; | |
359 case base::File::FILE_ERROR_NOT_A_DIRECTORY: | |
360 case base::File::FILE_ERROR_NOT_A_FILE: | |
361 return blink::WebFileErrorTypeMismatch; | |
362 case base::File::FILE_ERROR_ACCESS_DENIED: | |
363 return blink::WebFileErrorNoModificationAllowed; | |
364 case base::File::FILE_ERROR_FAILED: | |
365 return blink::WebFileErrorInvalidState; | |
366 case base::File::FILE_ERROR_ABORT: | |
367 return blink::WebFileErrorAbort; | |
368 case base::File::FILE_ERROR_SECURITY: | |
369 return blink::WebFileErrorSecurity; | |
370 case base::File::FILE_ERROR_NO_SPACE: | |
371 return blink::WebFileErrorQuotaExceeded; | |
372 case base::File::FILE_ERROR_INVALID_URL: | |
373 return blink::WebFileErrorEncoding; | |
374 default: | |
375 return blink::WebFileErrorInvalidModification; | |
376 } | |
377 } | |
378 | |
379 bool GetFileSystemPublicType( | |
380 const std::string type_string, | |
381 blink::WebFileSystemType* type) { | |
382 DCHECK(type); | |
383 if (type_string == "Temporary") { | |
384 *type = blink::WebFileSystemTypeTemporary; | |
385 return true; | |
386 } | |
387 if (type_string == "Persistent") { | |
388 *type = blink::WebFileSystemTypePersistent; | |
389 return true; | |
390 } | |
391 if (type_string == "Isolated") { | |
392 *type = blink::WebFileSystemTypeIsolated; | |
393 return true; | |
394 } | |
395 if (type_string == "External") { | |
396 *type = blink::WebFileSystemTypeExternal; | |
397 return true; | |
398 } | |
399 NOTREACHED(); | |
400 return false; | |
401 } | |
402 | |
403 std::string GetIsolatedFileSystemName(const GURL& origin_url, | |
404 const std::string& filesystem_id) { | |
405 std::string name( | |
406 storage::GetFileSystemName(origin_url, storage::kFileSystemTypeIsolated)); | |
407 name.append("_"); | |
408 name.append(filesystem_id); | |
409 return name; | |
410 } | |
411 | |
412 bool CrackIsolatedFileSystemName(const std::string& filesystem_name, | |
413 std::string* filesystem_id) { | |
414 DCHECK(filesystem_id); | |
415 | |
416 // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}. | |
417 std::string start_token(":"); | |
418 start_token = start_token.append( | |
419 GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_"); | |
420 // WebKit uses different case in its constant for isolated file system | |
421 // names, so we do a case insensitive compare by converting both strings | |
422 // to uppercase. | |
423 // TODO(benwells): Remove this when WebKit uses the same constant. | |
424 start_token = StringToUpperASCII(start_token); | |
425 std::string filesystem_name_upper = StringToUpperASCII(filesystem_name); | |
426 size_t pos = filesystem_name_upper.find(start_token); | |
427 if (pos == std::string::npos) | |
428 return false; | |
429 if (pos == 0) | |
430 return false; | |
431 | |
432 *filesystem_id = filesystem_name.substr(pos + start_token.length(), | |
433 std::string::npos); | |
434 if (filesystem_id->empty()) | |
435 return false; | |
436 | |
437 return true; | |
438 } | |
439 | |
440 bool ValidateIsolatedFileSystemId(const std::string& filesystem_id) { | |
441 const size_t kExpectedFileSystemIdSize = 32; | |
442 if (filesystem_id.size() != kExpectedFileSystemIdSize) | |
443 return false; | |
444 const std::string kExpectedChars("ABCDEF0123456789"); | |
445 return base::ContainsOnlyChars(filesystem_id, kExpectedChars); | |
446 } | |
447 | |
448 std::string GetIsolatedFileSystemRootURIString( | |
449 const GURL& origin_url, | |
450 const std::string& filesystem_id, | |
451 const std::string& optional_root_name) { | |
452 std::string root = GetFileSystemRootURI(origin_url, | |
453 kFileSystemTypeIsolated).spec(); | |
454 if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent()) | |
455 return std::string(); | |
456 root.append(net::EscapePath(filesystem_id)); | |
457 root.append("/"); | |
458 if (!optional_root_name.empty()) { | |
459 if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent()) | |
460 return std::string(); | |
461 root.append(net::EscapePath(optional_root_name)); | |
462 root.append("/"); | |
463 } | |
464 return root; | |
465 } | |
466 | |
467 std::string GetExternalFileSystemRootURIString( | |
468 const GURL& origin_url, | |
469 const std::string& mount_name) { | |
470 std::string root = GetFileSystemRootURI(origin_url, | |
471 kFileSystemTypeExternal).spec(); | |
472 if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent()) | |
473 return std::string(); | |
474 root.append(net::EscapePath(mount_name)); | |
475 root.append("/"); | |
476 return root; | |
477 } | |
478 | |
479 base::File::Error NetErrorToFileError(int error) { | |
480 switch (error) { | |
481 case net::OK: | |
482 return base::File::FILE_OK; | |
483 case net::ERR_ADDRESS_IN_USE: | |
484 return base::File::FILE_ERROR_IN_USE; | |
485 case net::ERR_FILE_EXISTS: | |
486 return base::File::FILE_ERROR_EXISTS; | |
487 case net::ERR_FILE_NOT_FOUND: | |
488 return base::File::FILE_ERROR_NOT_FOUND; | |
489 case net::ERR_ACCESS_DENIED: | |
490 return base::File::FILE_ERROR_ACCESS_DENIED; | |
491 case net::ERR_TOO_MANY_SOCKET_STREAMS: | |
492 return base::File::FILE_ERROR_TOO_MANY_OPENED; | |
493 case net::ERR_OUT_OF_MEMORY: | |
494 return base::File::FILE_ERROR_NO_MEMORY; | |
495 case net::ERR_FILE_NO_SPACE: | |
496 return base::File::FILE_ERROR_NO_SPACE; | |
497 case net::ERR_INVALID_ARGUMENT: | |
498 case net::ERR_INVALID_HANDLE: | |
499 return base::File::FILE_ERROR_INVALID_OPERATION; | |
500 case net::ERR_ABORTED: | |
501 case net::ERR_CONNECTION_ABORTED: | |
502 return base::File::FILE_ERROR_ABORT; | |
503 case net::ERR_ADDRESS_INVALID: | |
504 case net::ERR_INVALID_URL: | |
505 return base::File::FILE_ERROR_INVALID_URL; | |
506 default: | |
507 return base::File::FILE_ERROR_FAILED; | |
508 } | |
509 } | |
510 | |
511 } // namespace storage | |
OLD | NEW |