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].
BaseName(const base::FilePath & virtual_path)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
DirName(const base::FilePath & virtual_path)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
GetComponents(const base::FilePath & path,std::vector<base::FilePath::StringType> * components)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
GetComponentsUTF8Unsafe(const base::FilePath & path,std::vector<std::string> * components)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
GetNormalizedFilePath(const base::FilePath & path)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
IsAbsolute(const base::FilePath::StringType & path)138 bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) {
139 return path.find(kRoot) == 0;
140 }
141
IsRootPath(const base::FilePath & path)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
ParseFileSystemSchemeURL(const GURL & url,GURL * origin_url,FileSystemType * type,base::FilePath * virtual_path)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
GetFileSystemRootURI(const GURL & origin_url,FileSystemType type)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
GetFileSystemName(const GURL & origin_url,FileSystemType type)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
QuotaStorageTypeToFileSystemType(quota::StorageType storage_type)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
FileSystemTypeToQuotaStorageType(FileSystemType type)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
GetFileSystemTypeString(FileSystemType type)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
FilePathToString(const base::FilePath & file_path)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
StringToFilePath(const std::string & file_path_string)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
FileErrorToWebFileError(base::File::Error error_code)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
GetFileSystemPublicType(const std::string type_string,blink::WebFileSystemType * type)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
GetIsolatedFileSystemName(const GURL & origin_url,const std::string & filesystem_id)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
CrackIsolatedFileSystemName(const std::string & filesystem_name,std::string * filesystem_id)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
ValidateIsolatedFileSystemId(const std::string & filesystem_id)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
GetIsolatedFileSystemRootURIString(const GURL & origin_url,const std::string & filesystem_id,const std::string & optional_root_name)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
GetExternalFileSystemRootURIString(const GURL & origin_url,const std::string & mount_name)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
NetErrorToFileError(int error)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
513