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.
5 #include "storage/common/fileapi/file_system_util.h"
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 "storage/common/database/database_identifier.h"
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";
27 const base::FilePath::CharType
VirtualPath::kRoot
[] = FILE_PATH_LITERAL("/");
28 const base::FilePath::CharType
VirtualPath::kSeparator
= FILE_PATH_LITERAL('/');
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
34 base::FilePath
VirtualPath::BaseName(const base::FilePath
& virtual_path
) {
35 base::FilePath::StringType path
= virtual_path
.value();
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);
47 return base::FilePath(path
);
50 base::FilePath
VirtualPath::DirName(const base::FilePath
& virtual_path
) {
51 typedef base::FilePath::StringType StringType
;
52 StringType path
= virtual_path
.value();
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.
57 // Strip trailing separators.
58 while (path
.size() > 1 && base::FilePath::IsSeparator(path
[path
.size() - 1]))
59 path
.resize(path
.size() - 1);
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
);
67 if (last_separator
== 0) {
68 // path_ is in the root directory.
69 return base::FilePath(path
.substr(0, 1));
71 // path_ is somewhere else, trim the basename.
72 path
.resize(last_separator
);
74 // Strip trailing separators.
75 while (path
.size() > 1 && base::FilePath::IsSeparator(path
[path
.size() - 1]))
76 path
.resize(path
.size() - 1);
79 return base::FilePath(base::FilePath::kCurrentDirectory
);
81 return base::FilePath(path
);
84 void VirtualPath::GetComponents(
85 const base::FilePath
& path
,
86 std::vector
<base::FilePath::StringType
>* components
) {
87 typedef base::FilePath::StringType StringType
;
93 if (path
.value().empty())
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
);
107 void VirtualPath::GetComponentsUTF8Unsafe(
108 const base::FilePath
& path
,
109 std::vector
<std::string
>* components
) {
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();
120 components
->push_back(base::FilePath(*it
).AsUTF8Unsafe());
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
);
134 return (IsAbsolute(normalized_path
)) ?
135 normalized_path
: base::FilePath::StringType(kRoot
) + normalized_path
;
138 bool VirtualPath::IsAbsolute(const base::FilePath::StringType
& path
) {
139 return path
.find(kRoot
) == 0;
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
));
150 bool ParseFileSystemSchemeURL(const GURL
& url
,
152 FileSystemType
* type
,
153 base::FilePath
* virtual_path
) {
155 FileSystemType file_system_type
= kFileSystemTypeUnknown
;
157 if (!url
.is_valid() || !url
.SchemeIsFileSystem())
164 { kFileSystemTypePersistent
, kPersistentDir
},
165 { kFileSystemTypeTemporary
, kTemporaryDir
},
166 { kFileSystemTypeIsolated
, kIsolatedDir
},
167 { kFileSystemTypeExternal
, kExternalDir
},
168 { kFileSystemTypeTest
, kTestDir
},
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
;
181 if (file_system_type
== kFileSystemTypeUnknown
)
184 std::string path
= net::UnescapeURLComponent(url
.path(),
185 net::UnescapeRule::SPACES
| net::UnescapeRule::URL_SPECIAL_CHARS
|
186 net::UnescapeRule::CONTROL_CHARS
);
188 // Ensure the path is relative.
189 while (!path
.empty() && path
[0] == '/')
192 base::FilePath converted_path
= base::FilePath::FromUTF8Unsafe(path
);
194 // All parent references should have been resolved in the renderer.
195 if (converted_path
.ReferencesParent())
199 *origin_url
= url
.GetOrigin();
201 *type
= file_system_type
;
203 *virtual_path
= converted_path
.NormalizePathSeparators().
204 StripTrailingSeparators();
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());
214 std::string url
= "filesystem:" + origin_url
.GetWithEmptyPath().spec();
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.
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
;
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
;
259 return kFileSystemTypeUnknown
;
262 storage::StorageType
FileSystemTypeToQuotaStorageType(FileSystemType 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
;
274 return storage::kStorageTypeUnknown
;
278 std::string
GetFileSystemTypeString(FileSystemType type
) {
280 case kFileSystemTypeTemporary
:
282 case kFileSystemTypePersistent
:
284 case kFileSystemTypeIsolated
:
286 case kFileSystemTypeExternal
:
288 case kFileSystemTypeTest
:
290 case kFileSystemTypeNativeLocal
:
291 return "NativeLocal";
292 case kFileSystemTypeRestrictedNativeLocal
:
293 return "RestrictedNativeLocal";
294 case kFileSystemTypeDragged
:
296 case kFileSystemTypeNativeMedia
:
297 return "NativeMedia";
298 case kFileSystemTypeDeviceMedia
:
299 return "DeviceMedia";
300 case kFileSystemTypePicasa
:
302 case kFileSystemTypeItunes
:
304 case kFileSystemTypeIphoto
:
306 case kFileSystemTypeDrive
:
308 case kFileSystemTypeSyncable
:
309 case kFileSystemTypeSyncableForInternalSync
:
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
:
321 case kFileSystemTypeDeviceMediaAsFileStorage
:
322 return "DeviceMediaStorage";
323 case kFileSystemInternalTypeEnumStart
:
324 case kFileSystemInternalTypeEnumEnd
:
327 case kFileSystemTypeUnknown
:
331 return std::string();
334 std::string
FilePathToString(const base::FilePath
& file_path
) {
336 return base::UTF16ToUTF8(file_path
.value());
337 #elif defined(OS_POSIX)
338 return file_path
.value();
342 base::FilePath
StringToFilePath(const std::string
& file_path_string
) {
344 return base::FilePath(base::UTF8ToUTF16(file_path_string
));
345 #elif defined(OS_POSIX)
346 return base::FilePath(file_path_string
);
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
;
375 return blink::WebFileErrorInvalidModification
;
379 bool GetFileSystemPublicType(
380 const std::string type_string
,
381 blink::WebFileSystemType
* type
) {
383 if (type_string
== "Temporary") {
384 *type
= blink::WebFileSystemTypeTemporary
;
387 if (type_string
== "Persistent") {
388 *type
= blink::WebFileSystemTypePersistent
;
391 if (type_string
== "Isolated") {
392 *type
= blink::WebFileSystemTypeIsolated
;
395 if (type_string
== "External") {
396 *type
= blink::WebFileSystemTypeExternal
;
403 std::string
GetIsolatedFileSystemName(const GURL
& origin_url
,
404 const std::string
& filesystem_id
) {
406 storage::GetFileSystemName(origin_url
, storage::kFileSystemTypeIsolated
));
408 name
.append(filesystem_id
);
412 bool CrackIsolatedFileSystemName(const std::string
& filesystem_name
,
413 std::string
* filesystem_id
) {
414 DCHECK(filesystem_id
);
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
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
)
432 *filesystem_id
= filesystem_name
.substr(pos
+ start_token
.length(),
434 if (filesystem_id
->empty())
440 bool ValidateIsolatedFileSystemId(const std::string
& filesystem_id
) {
441 const size_t kExpectedFileSystemIdSize
= 32;
442 if (filesystem_id
.size() != kExpectedFileSystemIdSize
)
444 const std::string
kExpectedChars("ABCDEF0123456789");
445 return base::ContainsOnlyChars(filesystem_id
, kExpectedChars
);
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
));
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
));
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
));
479 base::File::Error
NetErrorToFileError(int error
) {
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
;
507 return base::File::FILE_ERROR_FAILED
;
511 } // namespace storage