• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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