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/net_errors.h"
15 #include "url/gurl.h"
16 #include "webkit/common/database/database_identifier.h"
17
18 namespace fileapi {
19
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";
25
26 const base::FilePath::CharType VirtualPath::kRoot[] = FILE_PATH_LITERAL("/");
27 const base::FilePath::CharType VirtualPath::kSeparator = FILE_PATH_LITERAL('/');
28
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
32 // replacement].
BaseName(const base::FilePath & virtual_path)33 base::FilePath VirtualPath::BaseName(const base::FilePath& virtual_path) {
34 base::FilePath::StringType path = virtual_path.value();
35
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);
45
46 return base::FilePath(path);
47 }
48
DirName(const base::FilePath & virtual_path)49 base::FilePath VirtualPath::DirName(const base::FilePath& virtual_path) {
50 typedef base::FilePath::StringType StringType;
51 StringType path = virtual_path.value();
52
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.
55
56 // Strip trailing separators.
57 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
58 path.resize(path.size() - 1);
59
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);
65 }
66 if (last_separator == 0) {
67 // path_ is in the root directory.
68 return base::FilePath(path.substr(0, 1));
69 }
70 // path_ is somewhere else, trim the basename.
71 path.resize(last_separator);
72
73 // Strip trailing separators.
74 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
75 path.resize(path.size() - 1);
76
77 if (path.empty())
78 return base::FilePath(base::FilePath::kCurrentDirectory);
79
80 return base::FilePath(path);
81 }
82
GetComponents(const base::FilePath & path,std::vector<base::FilePath::StringType> * components)83 void VirtualPath::GetComponents(
84 const base::FilePath& path,
85 std::vector<base::FilePath::StringType>* components) {
86 typedef base::FilePath::StringType StringType;
87
88 DCHECK(components);
89 if (!components)
90 return;
91 components->clear();
92 if (path.value().empty())
93 return;
94
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);
102 begin = end + 1;
103 }
104 }
105
GetComponentsUTF8Unsafe(const base::FilePath & path,std::vector<std::string> * components)106 void VirtualPath::GetComponentsUTF8Unsafe(
107 const base::FilePath& path,
108 std::vector<std::string>* components) {
109 DCHECK(components);
110 if (!components)
111 return;
112 components->clear();
113
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();
118 ++it) {
119 components->push_back(base::FilePath(*it).AsUTF8Unsafe());
120 }
121 }
122
GetNormalizedFilePath(const base::FilePath & path)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);
131 }
132
133 return (IsAbsolute(normalized_path)) ?
134 normalized_path : base::FilePath::StringType(kRoot) + normalized_path;
135 }
136
IsAbsolute(const base::FilePath::StringType & path)137 bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) {
138 return path.find(kRoot) == 0;
139 }
140
IsRootPath(const base::FilePath & path)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));
147 }
148
GetFileSystemRootURI(const GURL & origin_url,FileSystemType type)149 GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) {
150 // origin_url is based on a security origin, so http://foo.com or file:///
151 // instead of the corresponding filesystem URL.
152 DCHECK(!origin_url.SchemeIsFileSystem());
153
154 std::string url = "filesystem:" + origin_url.GetWithEmptyPath().spec();
155 switch (type) {
156 case kFileSystemTypeTemporary:
157 url += (kTemporaryDir + 1); // We don't want the leading slash.
158 return GURL(url + "/");
159 case kFileSystemTypePersistent:
160 url += (kPersistentDir + 1); // We don't want the leading slash.
161 return GURL(url + "/");
162 case kFileSystemTypeExternal:
163 url += (kExternalDir + 1); // We don't want the leading slash.
164 return GURL(url + "/");
165 case kFileSystemTypeIsolated:
166 url += (kIsolatedDir + 1); // We don't want the leading slash.
167 return GURL(url + "/");
168 case kFileSystemTypeTest:
169 url += (kTestDir + 1); // We don't want the leading slash.
170 return GURL(url + "/");
171 // Internal types are always pointed via isolated or external URLs.
172 default:
173 NOTREACHED();
174 }
175 NOTREACHED();
176 return GURL();
177 }
178
GetFileSystemName(const GURL & origin_url,FileSystemType type)179 std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) {
180 std::string origin_identifier =
181 webkit_database::GetIdentifierFromOrigin(origin_url);
182 std::string type_string = GetFileSystemTypeString(type);
183 DCHECK(!type_string.empty());
184 return origin_identifier + ":" + type_string;
185 }
186
QuotaStorageTypeToFileSystemType(quota::StorageType storage_type)187 FileSystemType QuotaStorageTypeToFileSystemType(
188 quota::StorageType storage_type) {
189 switch (storage_type) {
190 case quota::kStorageTypeTemporary:
191 return kFileSystemTypeTemporary;
192 case quota::kStorageTypePersistent:
193 return kFileSystemTypePersistent;
194 case quota::kStorageTypeSyncable:
195 return kFileSystemTypeSyncable;
196 case quota::kStorageTypeQuotaNotManaged:
197 case quota::kStorageTypeUnknown:
198 return kFileSystemTypeUnknown;
199 }
200 return kFileSystemTypeUnknown;
201 }
202
FileSystemTypeToQuotaStorageType(FileSystemType type)203 quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) {
204 switch (type) {
205 case kFileSystemTypeTemporary:
206 return quota::kStorageTypeTemporary;
207 case kFileSystemTypePersistent:
208 return quota::kStorageTypePersistent;
209 case kFileSystemTypeSyncable:
210 case kFileSystemTypeSyncableForInternalSync:
211 return quota::kStorageTypeSyncable;
212 case kFileSystemTypePluginPrivate:
213 return quota::kStorageTypeQuotaNotManaged;
214 default:
215 return quota::kStorageTypeUnknown;
216 }
217 }
218
GetFileSystemTypeString(FileSystemType type)219 std::string GetFileSystemTypeString(FileSystemType type) {
220 switch (type) {
221 case kFileSystemTypeTemporary:
222 return "Temporary";
223 case kFileSystemTypePersistent:
224 return "Persistent";
225 case kFileSystemTypeIsolated:
226 return "Isolated";
227 case kFileSystemTypeExternal:
228 return "External";
229 case kFileSystemTypeTest:
230 return "Test";
231 case kFileSystemTypeNativeLocal:
232 return "NativeLocal";
233 case kFileSystemTypeRestrictedNativeLocal:
234 return "RestrictedNativeLocal";
235 case kFileSystemTypeDragged:
236 return "Dragged";
237 case kFileSystemTypeNativeMedia:
238 return "NativeMedia";
239 case kFileSystemTypeDeviceMedia:
240 return "DeviceMedia";
241 case kFileSystemTypePicasa:
242 return "Picasa";
243 case kFileSystemTypeItunes:
244 return "Itunes";
245 case kFileSystemTypeIphoto:
246 return "Iphoto";
247 case kFileSystemTypeDrive:
248 return "Drive";
249 case kFileSystemTypeSyncable:
250 case kFileSystemTypeSyncableForInternalSync:
251 return "Syncable";
252 case kFileSystemTypeNativeForPlatformApp:
253 return "NativeForPlatformApp";
254 case kFileSystemTypeForTransientFile:
255 return "TransientFile";
256 case kFileSystemTypePluginPrivate:
257 return "PluginPrivate";
258 case kFileSystemInternalTypeEnumStart:
259 case kFileSystemInternalTypeEnumEnd:
260 NOTREACHED();
261 // Fall through.
262 case kFileSystemTypeUnknown:
263 return "Unknown";
264 }
265 NOTREACHED();
266 return std::string();
267 }
268
FilePathToString(const base::FilePath & file_path)269 std::string FilePathToString(const base::FilePath& file_path) {
270 #if defined(OS_WIN)
271 return UTF16ToUTF8(file_path.value());
272 #elif defined(OS_POSIX)
273 return file_path.value();
274 #endif
275 }
276
StringToFilePath(const std::string & file_path_string)277 base::FilePath StringToFilePath(const std::string& file_path_string) {
278 #if defined(OS_WIN)
279 return base::FilePath(UTF8ToUTF16(file_path_string));
280 #elif defined(OS_POSIX)
281 return base::FilePath(file_path_string);
282 #endif
283 }
284
PlatformFileErrorToWebFileError(base::PlatformFileError error_code)285 blink::WebFileError PlatformFileErrorToWebFileError(
286 base::PlatformFileError error_code) {
287 switch (error_code) {
288 case base::PLATFORM_FILE_ERROR_NOT_FOUND:
289 return blink::WebFileErrorNotFound;
290 case base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
291 case base::PLATFORM_FILE_ERROR_EXISTS:
292 case base::PLATFORM_FILE_ERROR_NOT_EMPTY:
293 return blink::WebFileErrorInvalidModification;
294 case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
295 case base::PLATFORM_FILE_ERROR_NOT_A_FILE:
296 return blink::WebFileErrorTypeMismatch;
297 case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
298 return blink::WebFileErrorNoModificationAllowed;
299 case base::PLATFORM_FILE_ERROR_FAILED:
300 return blink::WebFileErrorInvalidState;
301 case base::PLATFORM_FILE_ERROR_ABORT:
302 return blink::WebFileErrorAbort;
303 case base::PLATFORM_FILE_ERROR_SECURITY:
304 return blink::WebFileErrorSecurity;
305 case base::PLATFORM_FILE_ERROR_NO_SPACE:
306 return blink::WebFileErrorQuotaExceeded;
307 case base::PLATFORM_FILE_ERROR_INVALID_URL:
308 return blink::WebFileErrorEncoding;
309 default:
310 return blink::WebFileErrorInvalidModification;
311 }
312 }
313
GetFileSystemPublicType(const std::string type_string,blink::WebFileSystemType * type)314 bool GetFileSystemPublicType(
315 const std::string type_string,
316 blink::WebFileSystemType* type) {
317 DCHECK(type);
318 if (type_string == "Temporary") {
319 *type = blink::WebFileSystemTypeTemporary;
320 return true;
321 }
322 if (type_string == "Persistent") {
323 *type = blink::WebFileSystemTypePersistent;
324 return true;
325 }
326 if (type_string == "Isolated") {
327 *type = blink::WebFileSystemTypeIsolated;
328 return true;
329 }
330 if (type_string == "External") {
331 *type = blink::WebFileSystemTypeExternal;
332 return true;
333 }
334 NOTREACHED();
335 return false;
336 }
337
GetIsolatedFileSystemName(const GURL & origin_url,const std::string & filesystem_id)338 std::string GetIsolatedFileSystemName(const GURL& origin_url,
339 const std::string& filesystem_id) {
340 std::string name(fileapi::GetFileSystemName(
341 origin_url, fileapi::kFileSystemTypeIsolated));
342 name.append("_");
343 name.append(filesystem_id);
344 return name;
345 }
346
CrackIsolatedFileSystemName(const std::string & filesystem_name,std::string * filesystem_id)347 bool CrackIsolatedFileSystemName(const std::string& filesystem_name,
348 std::string* filesystem_id) {
349 DCHECK(filesystem_id);
350
351 // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}.
352 std::string start_token(":");
353 start_token = start_token.append(
354 GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_");
355 // WebKit uses different case in its constant for isolated file system
356 // names, so we do a case insensitive compare by converting both strings
357 // to uppercase.
358 // TODO(benwells): Remove this when WebKit uses the same constant.
359 start_token = StringToUpperASCII(start_token);
360 std::string filesystem_name_upper = StringToUpperASCII(filesystem_name);
361 size_t pos = filesystem_name_upper.find(start_token);
362 if (pos == std::string::npos)
363 return false;
364 if (pos == 0)
365 return false;
366
367 *filesystem_id = filesystem_name.substr(pos + start_token.length(),
368 std::string::npos);
369 if (filesystem_id->empty())
370 return false;
371
372 return true;
373 }
374
ValidateIsolatedFileSystemId(const std::string & filesystem_id)375 bool ValidateIsolatedFileSystemId(const std::string& filesystem_id) {
376 const size_t kExpectedFileSystemIdSize = 32;
377 if (filesystem_id.size() != kExpectedFileSystemIdSize)
378 return false;
379 const std::string kExpectedChars("ABCDEF0123456789");
380 return ContainsOnlyChars(filesystem_id, kExpectedChars);
381 }
382
GetIsolatedFileSystemRootURIString(const GURL & origin_url,const std::string & filesystem_id,const std::string & optional_root_name)383 std::string GetIsolatedFileSystemRootURIString(
384 const GURL& origin_url,
385 const std::string& filesystem_id,
386 const std::string& optional_root_name) {
387 std::string root = GetFileSystemRootURI(origin_url,
388 kFileSystemTypeIsolated).spec();
389 if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent())
390 return std::string();
391 root.append(filesystem_id);
392 root.append("/");
393 if (!optional_root_name.empty()) {
394 if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent())
395 return std::string();
396 root.append(optional_root_name);
397 root.append("/");
398 }
399 return root;
400 }
401
GetExternalFileSystemRootURIString(const GURL & origin_url,const std::string & mount_name)402 std::string GetExternalFileSystemRootURIString(
403 const GURL& origin_url,
404 const std::string& mount_name) {
405 std::string root = GetFileSystemRootURI(origin_url,
406 kFileSystemTypeExternal).spec();
407 if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent())
408 return std::string();
409 root.append(mount_name);
410 root.append("/");
411 return root;
412 }
413
NetErrorToPlatformFileError(int error)414 base::PlatformFileError NetErrorToPlatformFileError(int error) {
415 switch (error) {
416 case net::OK:
417 return base::PLATFORM_FILE_OK;
418 case net::ERR_ADDRESS_IN_USE:
419 return base::PLATFORM_FILE_ERROR_IN_USE;
420 case net::ERR_FILE_EXISTS:
421 return base::PLATFORM_FILE_ERROR_EXISTS;
422 case net::ERR_FILE_NOT_FOUND:
423 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
424 case net::ERR_ACCESS_DENIED:
425 return base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
426 case net::ERR_TOO_MANY_SOCKET_STREAMS:
427 return base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
428 case net::ERR_OUT_OF_MEMORY:
429 return base::PLATFORM_FILE_ERROR_NO_MEMORY;
430 case net::ERR_FILE_NO_SPACE:
431 return base::PLATFORM_FILE_ERROR_NO_SPACE;
432 case net::ERR_INVALID_ARGUMENT:
433 case net::ERR_INVALID_HANDLE:
434 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
435 case net::ERR_ABORTED:
436 case net::ERR_CONNECTION_ABORTED:
437 return base::PLATFORM_FILE_ERROR_ABORT;
438 case net::ERR_ADDRESS_INVALID:
439 case net::ERR_INVALID_URL:
440 return base::PLATFORM_FILE_ERROR_INVALID_URL;
441 default:
442 return base::PLATFORM_FILE_ERROR_FAILED;
443 }
444 }
445
446 #if defined(OS_CHROMEOS)
GetFileSystemInfoForChromeOS(const GURL & origin_url)447 FileSystemInfo GetFileSystemInfoForChromeOS(const GURL& origin_url) {
448 FileSystemType mount_type = fileapi::kFileSystemTypeExternal;
449 return FileSystemInfo(fileapi::GetFileSystemName(origin_url, mount_type),
450 fileapi::GetFileSystemRootURI(origin_url, mount_type),
451 mount_type);
452 }
453 #endif
454
455 } // namespace fileapi
456