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/logging.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "net/base/escape.h"
14 #include "net/base/net_errors.h"
15 #include "storage/common/database/database_identifier.h"
20 const char kPersistentDir
[] = "/persistent";
21 const char kTemporaryDir
[] = "/temporary";
22 const char kIsolatedDir
[] = "/isolated";
23 const char kExternalDir
[] = "/external";
24 const char kTestDir
[] = "/test";
26 const base::FilePath::CharType
VirtualPath::kRoot
[] = FILE_PATH_LITERAL("/");
27 const base::FilePath::CharType
VirtualPath::kSeparator
= FILE_PATH_LITERAL('/');
29 // TODO(ericu): Consider removing support for '\', even on Windows, if possible.
30 // There's a lot of test code that will need reworking, and we may have trouble
31 // with base::FilePath elsewhere [e.g. DirName and other methods may also need
33 base::FilePath
VirtualPath::BaseName(const base::FilePath
& virtual_path
) {
34 base::FilePath::StringType path
= virtual_path
.value();
36 // Keep everything after the final separator, but if the pathname is only
37 // one character and it's a separator, leave it alone.
38 while (path
.size() > 1 && base::FilePath::IsSeparator(path
[path
.size() - 1]))
39 path
.resize(path
.size() - 1);
40 base::FilePath::StringType::size_type last_separator
=
41 path
.find_last_of(base::FilePath::kSeparators
);
42 if (last_separator
!= base::FilePath::StringType::npos
&&
43 last_separator
< path
.size() - 1)
44 path
.erase(0, last_separator
+ 1);
46 return base::FilePath(path
);
49 base::FilePath
VirtualPath::DirName(const base::FilePath
& virtual_path
) {
50 typedef base::FilePath::StringType StringType
;
51 StringType path
= virtual_path
.value();
53 // The logic below is taken from that of base::FilePath::DirName, except
54 // that this version never cares about '//' or drive-letters even on win32.
56 // Strip trailing separators.
57 while (path
.size() > 1 && base::FilePath::IsSeparator(path
[path
.size() - 1]))
58 path
.resize(path
.size() - 1);
60 StringType::size_type last_separator
=
61 path
.find_last_of(base::FilePath::kSeparators
);
62 if (last_separator
== StringType::npos
) {
63 // path_ is in the current directory.
64 return base::FilePath(base::FilePath::kCurrentDirectory
);
66 if (last_separator
== 0) {
67 // path_ is in the root directory.
68 return base::FilePath(path
.substr(0, 1));
70 // path_ is somewhere else, trim the basename.
71 path
.resize(last_separator
);
73 // Strip trailing separators.
74 while (path
.size() > 1 && base::FilePath::IsSeparator(path
[path
.size() - 1]))
75 path
.resize(path
.size() - 1);
78 return base::FilePath(base::FilePath::kCurrentDirectory
);
80 return base::FilePath(path
);
83 void VirtualPath::GetComponents(
84 const base::FilePath
& path
,
85 std::vector
<base::FilePath::StringType
>* components
) {
86 typedef base::FilePath::StringType StringType
;
92 if (path
.value().empty())
95 StringType::size_type begin
= 0, end
= 0;
96 while (begin
< path
.value().length() && end
!= StringType::npos
) {
97 end
= path
.value().find_first_of(base::FilePath::kSeparators
, begin
);
98 StringType component
= path
.value().substr(
99 begin
, end
== StringType::npos
? StringType::npos
: end
- begin
);
100 if (!component
.empty() && component
!= base::FilePath::kCurrentDirectory
)
101 components
->push_back(component
);
106 void VirtualPath::GetComponentsUTF8Unsafe(
107 const base::FilePath
& path
,
108 std::vector
<std::string
>* components
) {
114 std::vector
<base::FilePath::StringType
> stringtype_components
;
115 VirtualPath::GetComponents(path
, &stringtype_components
);
116 std::vector
<base::FilePath::StringType
>::const_iterator it
;
117 for (it
= stringtype_components
.begin(); it
!= stringtype_components
.end();
119 components
->push_back(base::FilePath(*it
).AsUTF8Unsafe());
123 base::FilePath::StringType
VirtualPath::GetNormalizedFilePath(
124 const base::FilePath
& path
) {
125 base::FilePath::StringType normalized_path
= path
.value();
126 const size_t num_separators
= base::FilePath::StringType(
127 base::FilePath::kSeparators
).length();
128 for (size_t i
= 0; i
< num_separators
; ++i
) {
129 std::replace(normalized_path
.begin(), normalized_path
.end(),
130 base::FilePath::kSeparators
[i
], kSeparator
);
133 return (IsAbsolute(normalized_path
)) ?
134 normalized_path
: base::FilePath::StringType(kRoot
) + normalized_path
;
137 bool VirtualPath::IsAbsolute(const base::FilePath::StringType
& path
) {
138 return path
.find(kRoot
) == 0;
141 bool VirtualPath::IsRootPath(const base::FilePath
& path
) {
142 std::vector
<base::FilePath::StringType
> components
;
143 VirtualPath::GetComponents(path
, &components
);
144 return (path
.empty() || components
.empty() ||
145 (components
.size() == 1 &&
146 components
[0] == VirtualPath::kRoot
));
149 bool ParseFileSystemSchemeURL(const GURL
& url
,
151 FileSystemType
* type
,
152 base::FilePath
* virtual_path
) {
154 FileSystemType file_system_type
= kFileSystemTypeUnknown
;
156 if (!url
.is_valid() || !url
.SchemeIsFileSystem())
163 { kFileSystemTypePersistent
, kPersistentDir
},
164 { kFileSystemTypeTemporary
, kTemporaryDir
},
165 { kFileSystemTypeIsolated
, kIsolatedDir
},
166 { kFileSystemTypeExternal
, kExternalDir
},
167 { kFileSystemTypeTest
, kTestDir
},
170 // A path of the inner_url contains only mount type part (e.g. "/temporary").
171 DCHECK(url
.inner_url());
172 std::string inner_path
= url
.inner_url()->path();
173 for (size_t i
= 0; i
< arraysize(kValidTypes
); ++i
) {
174 if (inner_path
== kValidTypes
[i
].dir
) {
175 file_system_type
= kValidTypes
[i
].type
;
180 if (file_system_type
== kFileSystemTypeUnknown
)
183 std::string path
= net::UnescapeURLComponent(url
.path(),
184 net::UnescapeRule::SPACES
| net::UnescapeRule::URL_SPECIAL_CHARS
|
185 net::UnescapeRule::SPOOFING_AND_CONTROL_CHARS
);
187 // Ensure the path is relative.
188 while (!path
.empty() && path
[0] == '/')
191 base::FilePath converted_path
= base::FilePath::FromUTF8Unsafe(path
);
193 // All parent references should have been resolved in the renderer.
194 if (converted_path
.ReferencesParent())
198 *origin_url
= url
.GetOrigin();
200 *type
= file_system_type
;
202 *virtual_path
= converted_path
.NormalizePathSeparators().
203 StripTrailingSeparators();
208 GURL
GetFileSystemRootURI(const GURL
& origin_url
, FileSystemType type
) {
209 // origin_url is based on a security origin, so http://foo.com or file:///
210 // instead of the corresponding filesystem URL.
211 DCHECK(!origin_url
.SchemeIsFileSystem());
213 std::string url
= "filesystem:" + origin_url
.GetWithEmptyPath().spec();
215 case kFileSystemTypeTemporary
:
216 url
+= (kTemporaryDir
+ 1); // We don't want the leading slash.
217 return GURL(url
+ "/");
218 case kFileSystemTypePersistent
:
219 url
+= (kPersistentDir
+ 1); // We don't want the leading slash.
220 return GURL(url
+ "/");
221 case kFileSystemTypeExternal
:
222 url
+= (kExternalDir
+ 1); // We don't want the leading slash.
223 return GURL(url
+ "/");
224 case kFileSystemTypeIsolated
:
225 url
+= (kIsolatedDir
+ 1); // We don't want the leading slash.
226 return GURL(url
+ "/");
227 case kFileSystemTypeTest
:
228 url
+= (kTestDir
+ 1); // We don't want the leading slash.
229 return GURL(url
+ "/");
230 // Internal types are always pointed via isolated or external URLs.
238 std::string
GetFileSystemName(const GURL
& origin_url
, FileSystemType type
) {
239 std::string origin_identifier
= storage::GetIdentifierFromOrigin(origin_url
);
240 std::string type_string
= GetFileSystemTypeString(type
);
241 DCHECK(!type_string
.empty());
242 return origin_identifier
+ ":" + type_string
;
245 FileSystemType
QuotaStorageTypeToFileSystemType(
246 storage::StorageType storage_type
) {
247 switch (storage_type
) {
248 case storage::kStorageTypeTemporary
:
249 return kFileSystemTypeTemporary
;
250 case storage::kStorageTypePersistent
:
251 return kFileSystemTypePersistent
;
252 case storage::kStorageTypeSyncable
:
253 return kFileSystemTypeSyncable
;
254 case storage::kStorageTypeQuotaNotManaged
:
255 case storage::kStorageTypeUnknown
:
256 return kFileSystemTypeUnknown
;
258 return kFileSystemTypeUnknown
;
261 storage::StorageType
FileSystemTypeToQuotaStorageType(FileSystemType type
) {
263 case kFileSystemTypeTemporary
:
264 return storage::kStorageTypeTemporary
;
265 case kFileSystemTypePersistent
:
266 return storage::kStorageTypePersistent
;
267 case kFileSystemTypeSyncable
:
268 case kFileSystemTypeSyncableForInternalSync
:
269 return storage::kStorageTypeSyncable
;
270 case kFileSystemTypePluginPrivate
:
271 return storage::kStorageTypeQuotaNotManaged
;
273 return storage::kStorageTypeUnknown
;
277 std::string
GetFileSystemTypeString(FileSystemType type
) {
279 case kFileSystemTypeTemporary
:
281 case kFileSystemTypePersistent
:
283 case kFileSystemTypeIsolated
:
285 case kFileSystemTypeExternal
:
287 case kFileSystemTypeTest
:
289 case kFileSystemTypeNativeLocal
:
290 return "NativeLocal";
291 case kFileSystemTypeRestrictedNativeLocal
:
292 return "RestrictedNativeLocal";
293 case kFileSystemTypeDragged
:
295 case kFileSystemTypeNativeMedia
:
296 return "NativeMedia";
297 case kFileSystemTypeDeviceMedia
:
298 return "DeviceMedia";
299 case kFileSystemTypePicasa
:
301 case kFileSystemTypeItunes
:
303 case kFileSystemTypeIphoto
:
305 case kFileSystemTypeDrive
:
307 case kFileSystemTypeSyncable
:
308 case kFileSystemTypeSyncableForInternalSync
:
310 case kFileSystemTypeNativeForPlatformApp
:
311 return "NativeForPlatformApp";
312 case kFileSystemTypeForTransientFile
:
313 return "TransientFile";
314 case kFileSystemTypePluginPrivate
:
315 return "PluginPrivate";
316 case kFileSystemTypeCloudDevice
:
317 return "CloudDevice";
318 case kFileSystemTypeProvided
:
320 case kFileSystemTypeDeviceMediaAsFileStorage
:
321 return "DeviceMediaStorage";
322 case kFileSystemInternalTypeEnumStart
:
323 case kFileSystemInternalTypeEnumEnd
:
326 case kFileSystemTypeUnknown
:
330 return std::string();
333 std::string
FilePathToString(const base::FilePath
& file_path
) {
335 return base::UTF16ToUTF8(file_path
.value());
336 #elif defined(OS_POSIX)
337 return file_path
.value();
341 base::FilePath
StringToFilePath(const std::string
& file_path_string
) {
343 return base::FilePath(base::UTF8ToUTF16(file_path_string
));
344 #elif defined(OS_POSIX)
345 return base::FilePath(file_path_string
);
349 blink::WebFileError
FileErrorToWebFileError(
350 base::File::Error error_code
) {
351 switch (error_code
) {
352 case base::File::FILE_ERROR_NOT_FOUND
:
353 return blink::WebFileErrorNotFound
;
354 case base::File::FILE_ERROR_INVALID_OPERATION
:
355 case base::File::FILE_ERROR_EXISTS
:
356 case base::File::FILE_ERROR_NOT_EMPTY
:
357 return blink::WebFileErrorInvalidModification
;
358 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
359 case base::File::FILE_ERROR_NOT_A_FILE
:
360 return blink::WebFileErrorTypeMismatch
;
361 case base::File::FILE_ERROR_ACCESS_DENIED
:
362 return blink::WebFileErrorNoModificationAllowed
;
363 case base::File::FILE_ERROR_FAILED
:
364 return blink::WebFileErrorInvalidState
;
365 case base::File::FILE_ERROR_ABORT
:
366 return blink::WebFileErrorAbort
;
367 case base::File::FILE_ERROR_SECURITY
:
368 return blink::WebFileErrorSecurity
;
369 case base::File::FILE_ERROR_NO_SPACE
:
370 return blink::WebFileErrorQuotaExceeded
;
371 case base::File::FILE_ERROR_INVALID_URL
:
372 return blink::WebFileErrorEncoding
;
374 return blink::WebFileErrorInvalidModification
;
378 bool GetFileSystemPublicType(
379 const std::string type_string
,
380 blink::WebFileSystemType
* type
) {
382 if (type_string
== "Temporary") {
383 *type
= blink::WebFileSystemTypeTemporary
;
386 if (type_string
== "Persistent") {
387 *type
= blink::WebFileSystemTypePersistent
;
390 if (type_string
== "Isolated") {
391 *type
= blink::WebFileSystemTypeIsolated
;
394 if (type_string
== "External") {
395 *type
= blink::WebFileSystemTypeExternal
;
402 std::string
GetIsolatedFileSystemName(const GURL
& origin_url
,
403 const std::string
& filesystem_id
) {
405 storage::GetFileSystemName(origin_url
, storage::kFileSystemTypeIsolated
));
407 name
.append(filesystem_id
);
411 bool CrackIsolatedFileSystemName(const std::string
& filesystem_name
,
412 std::string
* filesystem_id
) {
413 DCHECK(filesystem_id
);
415 // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}.
416 std::string
start_token(":");
417 start_token
= start_token
.append(
418 GetFileSystemTypeString(kFileSystemTypeIsolated
)).append("_");
419 // WebKit uses different case in its constant for isolated file system
420 // names, so we do a case insensitive compare by converting both strings
422 // TODO(benwells): Remove this when WebKit uses the same constant.
423 start_token
= base::ToUpperASCII(start_token
);
424 std::string filesystem_name_upper
= base::ToUpperASCII(filesystem_name
);
425 size_t pos
= filesystem_name_upper
.find(start_token
);
426 if (pos
== std::string::npos
)
431 *filesystem_id
= filesystem_name
.substr(pos
+ start_token
.length(),
433 if (filesystem_id
->empty())
439 bool ValidateIsolatedFileSystemId(const std::string
& filesystem_id
) {
440 const size_t kExpectedFileSystemIdSize
= 32;
441 if (filesystem_id
.size() != kExpectedFileSystemIdSize
)
443 const std::string
kExpectedChars("ABCDEF0123456789");
444 return base::ContainsOnlyChars(filesystem_id
, kExpectedChars
);
447 std::string
GetIsolatedFileSystemRootURIString(
448 const GURL
& origin_url
,
449 const std::string
& filesystem_id
,
450 const std::string
& optional_root_name
) {
451 std::string root
= GetFileSystemRootURI(origin_url
,
452 kFileSystemTypeIsolated
).spec();
453 if (base::FilePath::FromUTF8Unsafe(filesystem_id
).ReferencesParent())
454 return std::string();
455 root
.append(net::EscapePath(filesystem_id
));
457 if (!optional_root_name
.empty()) {
458 if (base::FilePath::FromUTF8Unsafe(optional_root_name
).ReferencesParent())
459 return std::string();
460 root
.append(net::EscapePath(optional_root_name
));
466 std::string
GetExternalFileSystemRootURIString(
467 const GURL
& origin_url
,
468 const std::string
& mount_name
) {
469 std::string root
= GetFileSystemRootURI(origin_url
,
470 kFileSystemTypeExternal
).spec();
471 if (base::FilePath::FromUTF8Unsafe(mount_name
).ReferencesParent())
472 return std::string();
473 root
.append(net::EscapePath(mount_name
));
478 base::File::Error
NetErrorToFileError(int error
) {
481 return base::File::FILE_OK
;
482 case net::ERR_ADDRESS_IN_USE
:
483 return base::File::FILE_ERROR_IN_USE
;
484 case net::ERR_FILE_EXISTS
:
485 return base::File::FILE_ERROR_EXISTS
;
486 case net::ERR_FILE_NOT_FOUND
:
487 return base::File::FILE_ERROR_NOT_FOUND
;
488 case net::ERR_ACCESS_DENIED
:
489 return base::File::FILE_ERROR_ACCESS_DENIED
;
490 case net::ERR_OUT_OF_MEMORY
:
491 return base::File::FILE_ERROR_NO_MEMORY
;
492 case net::ERR_FILE_NO_SPACE
:
493 return base::File::FILE_ERROR_NO_SPACE
;
494 case net::ERR_INVALID_ARGUMENT
:
495 case net::ERR_INVALID_HANDLE
:
496 return base::File::FILE_ERROR_INVALID_OPERATION
;
497 case net::ERR_ABORTED
:
498 case net::ERR_CONNECTION_ABORTED
:
499 return base::File::FILE_ERROR_ABORT
;
500 case net::ERR_ADDRESS_INVALID
:
501 case net::ERR_INVALID_URL
:
502 return base::File::FILE_ERROR_INVALID_URL
;
504 return base::File::FILE_ERROR_FAILED
;
508 } // namespace storage