file_system_util.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 "googleurl/src/gurl.h"
15#include "webkit/common/database/database_identifier.h"
16
17namespace fileapi {
18
19const char kPersistentDir[] = "/persistent";
20const char kTemporaryDir[] = "/temporary";
21const char kIsolatedDir[] = "/isolated";
22const char kExternalDir[] = "/external";
23const char kTestDir[] = "/test";
24
25const base::FilePath::CharType VirtualPath::kRoot[] = FILE_PATH_LITERAL("/");
26const base::FilePath::CharType VirtualPath::kSeparator = FILE_PATH_LITERAL('/');
27
28// TODO(ericu): Consider removing support for '\', even on Windows, if possible.
29// There's a lot of test code that will need reworking, and we may have trouble
30// with base::FilePath elsewhere [e.g. DirName and other methods may also need
31// replacement].
32base::FilePath VirtualPath::BaseName(const base::FilePath& virtual_path) {
33  base::FilePath::StringType path = virtual_path.value();
34
35  // Keep everything after the final separator, but if the pathname is only
36  // one character and it's a separator, leave it alone.
37  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
38    path.resize(path.size() - 1);
39  base::FilePath::StringType::size_type last_separator =
40      path.find_last_of(base::FilePath::kSeparators);
41  if (last_separator != base::FilePath::StringType::npos &&
42      last_separator < path.size() - 1)
43    path.erase(0, last_separator + 1);
44
45  return base::FilePath(path);
46}
47
48base::FilePath VirtualPath::DirName(const base::FilePath& virtual_path) {
49  typedef base::FilePath::StringType StringType;
50  StringType  path = virtual_path.value();
51
52  // The logic below is taken from that of base::FilePath::DirName, except
53  // that this version never cares about '//' or drive-letters even on win32.
54
55  // Strip trailing separators.
56  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
57    path.resize(path.size() - 1);
58
59  StringType::size_type last_separator =
60      path.find_last_of(base::FilePath::kSeparators);
61  if (last_separator == StringType::npos) {
62    // path_ is in the current directory.
63    return base::FilePath(base::FilePath::kCurrentDirectory);
64  }
65  if (last_separator == 0) {
66    // path_ is in the root directory.
67    return base::FilePath(path.substr(0, 1));
68  }
69  // path_ is somewhere else, trim the basename.
70  path.resize(last_separator);
71
72  // Strip trailing separators.
73  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
74    path.resize(path.size() - 1);
75
76  if (path.empty())
77    return base::FilePath(base::FilePath::kCurrentDirectory);
78
79  return base::FilePath(path);
80}
81
82void VirtualPath::GetComponents(
83    const base::FilePath& path,
84    std::vector<base::FilePath::StringType>* components) {
85  typedef base::FilePath::StringType StringType;
86
87  DCHECK(components);
88  if (!components)
89    return;
90  components->clear();
91  if (path.value().empty())
92    return;
93
94  StringType::size_type begin = 0, end = 0;
95  while (begin < path.value().length() && end != StringType::npos) {
96    end = path.value().find_first_of(base::FilePath::kSeparators, begin);
97    StringType component = path.value().substr(
98        begin, end == StringType::npos ? StringType::npos : end - begin);
99    if (!component.empty() && component != base::FilePath::kCurrentDirectory)
100      components->push_back(component);
101    begin = end + 1;
102  }
103}
104
105void VirtualPath::GetComponentsUTF8Unsafe(
106    const base::FilePath& path,
107    std::vector<std::string>* components) {
108  DCHECK(components);
109  if (!components)
110    return;
111  components->clear();
112
113  std::vector<base::FilePath::StringType> stringtype_components;
114  VirtualPath::GetComponents(path, &stringtype_components);
115  std::vector<base::FilePath::StringType>::const_iterator it;
116  for (it = stringtype_components.begin(); it != stringtype_components.end();
117       ++it) {
118    components->push_back(base::FilePath(*it).AsUTF8Unsafe());
119  }
120}
121
122base::FilePath::StringType VirtualPath::GetNormalizedFilePath(
123    const base::FilePath& path) {
124  base::FilePath::StringType normalized_path = path.value();
125  const size_t num_separators = base::FilePath::StringType(
126      base::FilePath::kSeparators).length();
127  for (size_t i = 0; i < num_separators; ++i) {
128    std::replace(normalized_path.begin(), normalized_path.end(),
129                 base::FilePath::kSeparators[i], kSeparator);
130  }
131
132  return (IsAbsolute(normalized_path)) ?
133      normalized_path : base::FilePath::StringType(kRoot) + normalized_path;
134}
135
136bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) {
137  return path.find(kRoot) == 0;
138}
139
140GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) {
141  // origin_url is based on a security origin, so http://foo.com or file:///
142  // instead of the corresponding filesystem URL.
143  DCHECK(!origin_url.SchemeIsFileSystem());
144
145  std::string url = "filesystem:" + origin_url.GetWithEmptyPath().spec();
146  switch (type) {
147  case kFileSystemTypeTemporary:
148    url += (kTemporaryDir + 1);  // We don't want the leading slash.
149    return GURL(url + "/");
150  case kFileSystemTypePersistent:
151    url += (kPersistentDir + 1);  // We don't want the leading slash.
152    return GURL(url + "/");
153  case kFileSystemTypeExternal:
154    url += (kExternalDir + 1);  // We don't want the leading slash.
155    return GURL(url + "/");
156  case kFileSystemTypeIsolated:
157    url += (kIsolatedDir + 1);  // We don't want the leading slash.
158    return GURL(url + "/");
159  case kFileSystemTypeTest:
160    url += (kTestDir + 1);  // We don't want the leading slash.
161    return GURL(url + "/");
162  // Internal types are always pointed via isolated or external URLs.
163  default:
164    NOTREACHED();
165  }
166  NOTREACHED();
167  return GURL();
168}
169
170std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) {
171  std::string origin_identifier =
172      webkit_database::GetIdentifierFromOrigin(origin_url);
173  std::string type_string = GetFileSystemTypeString(type);
174  DCHECK(!type_string.empty());
175  return origin_identifier + ":" + type_string;
176}
177
178FileSystemType QuotaStorageTypeToFileSystemType(
179    quota::StorageType storage_type) {
180  switch (storage_type) {
181    case quota::kStorageTypeTemporary:
182      return kFileSystemTypeTemporary;
183    case quota::kStorageTypePersistent:
184      return kFileSystemTypePersistent;
185    case quota::kStorageTypeSyncable:
186      return kFileSystemTypeSyncable;
187    case quota::kStorageTypeUnknown:
188      return kFileSystemTypeUnknown;
189  }
190  return kFileSystemTypeUnknown;
191}
192
193quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) {
194  switch (type) {
195    case kFileSystemTypeTemporary:
196      return quota::kStorageTypeTemporary;
197    case kFileSystemTypePersistent:
198      return quota::kStorageTypePersistent;
199    case kFileSystemTypeSyncable:
200    case kFileSystemTypeSyncableForInternalSync:
201      return quota::kStorageTypeSyncable;
202    default:
203      return quota::kStorageTypeUnknown;
204  }
205}
206
207std::string GetFileSystemTypeString(FileSystemType type) {
208  switch (type) {
209    case kFileSystemTypeTemporary:
210      return "Temporary";
211    case kFileSystemTypePersistent:
212      return "Persistent";
213    case kFileSystemTypeIsolated:
214      return "Isolated";
215    case kFileSystemTypeExternal:
216      return "External";
217    case kFileSystemTypeTest:
218      return "Test";
219    case kFileSystemTypeNativeLocal:
220      return "NativeLocal";
221    case kFileSystemTypeRestrictedNativeLocal:
222      return "RestrictedNativeLocal";
223    case kFileSystemTypeDragged:
224      return "Dragged";
225    case kFileSystemTypeNativeMedia:
226      return "NativeMedia";
227    case kFileSystemTypeDeviceMedia:
228      return "DeviceMedia";
229    case kFileSystemTypePicasa:
230      return "Picasa";
231    case kFileSystemTypeItunes:
232      return "Itunes";
233    case kFileSystemTypeDrive:
234      return "Drive";
235    case kFileSystemTypeSyncable:
236    case kFileSystemTypeSyncableForInternalSync:
237      return "Syncable";
238    case kFileSystemTypeNativeForPlatformApp:
239      return "NativeForPlatformApp";
240    case kFileSystemTypeForTransientFile:
241      return "TransientFile";
242    case kFileSystemInternalTypeEnumStart:
243    case kFileSystemInternalTypeEnumEnd:
244      NOTREACHED();
245      // Fall through.
246    case kFileSystemTypeUnknown:
247      return "Unknown";
248  }
249  NOTREACHED();
250  return std::string();
251}
252
253std::string FilePathToString(const base::FilePath& file_path) {
254#if defined(OS_WIN)
255  return UTF16ToUTF8(file_path.value());
256#elif defined(OS_POSIX)
257  return file_path.value();
258#endif
259}
260
261base::FilePath StringToFilePath(const std::string& file_path_string) {
262#if defined(OS_WIN)
263  return base::FilePath(UTF8ToUTF16(file_path_string));
264#elif defined(OS_POSIX)
265  return base::FilePath(file_path_string);
266#endif
267}
268
269WebKit::WebFileError PlatformFileErrorToWebFileError(
270    base::PlatformFileError error_code) {
271  switch (error_code) {
272    case base::PLATFORM_FILE_ERROR_NOT_FOUND:
273      return WebKit::WebFileErrorNotFound;
274    case base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
275    case base::PLATFORM_FILE_ERROR_EXISTS:
276    case base::PLATFORM_FILE_ERROR_NOT_EMPTY:
277      return WebKit::WebFileErrorInvalidModification;
278    case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
279    case base::PLATFORM_FILE_ERROR_NOT_A_FILE:
280      return WebKit::WebFileErrorTypeMismatch;
281    case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
282      return WebKit::WebFileErrorNoModificationAllowed;
283    case base::PLATFORM_FILE_ERROR_FAILED:
284      return WebKit::WebFileErrorInvalidState;
285    case base::PLATFORM_FILE_ERROR_ABORT:
286      return WebKit::WebFileErrorAbort;
287    case base::PLATFORM_FILE_ERROR_SECURITY:
288      return WebKit::WebFileErrorSecurity;
289    case base::PLATFORM_FILE_ERROR_NO_SPACE:
290      return WebKit::WebFileErrorQuotaExceeded;
291    default:
292      return WebKit::WebFileErrorInvalidModification;
293  }
294}
295
296bool GetFileSystemPublicType(
297    const std::string type_string,
298    WebKit::WebFileSystemType* type
299) {
300  DCHECK(type);
301  if (type_string == "Temporary") {
302    *type = WebKit::WebFileSystemTypeTemporary;
303    return true;
304  }
305  if (type_string == "Persistent") {
306    *type = WebKit::WebFileSystemTypePersistent;
307    return true;
308  }
309  if (type_string == "Isolated") {
310    *type = WebKit::WebFileSystemTypeIsolated;
311    return true;
312  }
313  if (type_string == "External") {
314    *type = WebKit::WebFileSystemTypeExternal;
315    return true;
316  }
317  NOTREACHED();
318  return false;
319}
320
321std::string GetIsolatedFileSystemName(const GURL& origin_url,
322                                      const std::string& filesystem_id) {
323  std::string name(fileapi::GetFileSystemName(origin_url,
324      fileapi::kFileSystemTypeIsolated));
325  name.append("_");
326  name.append(filesystem_id);
327  return name;
328}
329
330bool CrackIsolatedFileSystemName(const std::string& filesystem_name,
331                                 std::string* filesystem_id) {
332  DCHECK(filesystem_id);
333
334  // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}.
335  std::string start_token(":");
336  start_token = start_token.append(
337      GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_");
338  // WebKit uses different case in its constant for isolated file system
339  // names, so we do a case insensitive compare by converting both strings
340  // to uppercase.
341  // TODO(benwells): Remove this when WebKit uses the same constant.
342  start_token = StringToUpperASCII(start_token);
343  std::string filesystem_name_upper = StringToUpperASCII(filesystem_name);
344  size_t pos = filesystem_name_upper.find(start_token);
345  if (pos == std::string::npos)
346    return false;
347  if (pos == 0)
348    return false;
349
350  *filesystem_id = filesystem_name.substr(pos + start_token.length(),
351                                          std::string::npos);
352  if (filesystem_id->empty())
353    return false;
354
355  return true;
356}
357
358std::string GetIsolatedFileSystemRootURIString(
359    const GURL& origin_url,
360    const std::string& filesystem_id,
361    const std::string& optional_root_name) {
362  std::string root = GetFileSystemRootURI(origin_url,
363                                          kFileSystemTypeIsolated).spec();
364  if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent())
365    return std::string();
366  root.append(filesystem_id);
367  root.append("/");
368  if (!optional_root_name.empty()) {
369    if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent())
370      return std::string();
371    root.append(optional_root_name);
372    root.append("/");
373  }
374  return root;
375}
376
377std::string GetExternalFileSystemRootURIString(
378    const GURL& origin_url,
379    const std::string& mount_name) {
380  std::string root = GetFileSystemRootURI(origin_url,
381                                          kFileSystemTypeExternal).spec();
382  if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent())
383    return std::string();
384  root.append(mount_name);
385  root.append("/");
386  return root;
387}
388
389}  // namespace fileapi
390