1// Copyright 2013 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/browser/fileapi/sandbox_file_system_backend_delegate.h"
6
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/file_util.h"
11#include "base/metrics/histogram.h"
12#include "base/stl_util.h"
13#include "base/task_runner_util.h"
14#include "net/base/net_util.h"
15#include "webkit/browser/blob/file_stream_reader.h"
16#include "webkit/browser/fileapi/async_file_util_adapter.h"
17#include "webkit/browser/fileapi/file_system_context.h"
18#include "webkit/browser/fileapi/file_system_operation_context.h"
19#include "webkit/browser/fileapi/file_system_url.h"
20#include "webkit/browser/fileapi/file_system_usage_cache.h"
21#include "webkit/browser/fileapi/obfuscated_file_util.h"
22#include "webkit/browser/fileapi/quota/quota_backend_impl.h"
23#include "webkit/browser/fileapi/quota/quota_reservation.h"
24#include "webkit/browser/fileapi/quota/quota_reservation_manager.h"
25#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
26#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
27#include "webkit/browser/fileapi/sandbox_quota_observer.h"
28#include "webkit/browser/quota/quota_manager_proxy.h"
29#include "webkit/common/fileapi/file_system_util.h"
30
31namespace fileapi {
32
33namespace {
34
35const char kTemporaryOriginsCountLabel[] = "FileSystem.TemporaryOriginsCount";
36const char kPersistentOriginsCountLabel[] = "FileSystem.PersistentOriginsCount";
37
38const char kOpenFileSystemLabel[] = "FileSystem.OpenFileSystem";
39const char kOpenFileSystemDetailLabel[] = "FileSystem.OpenFileSystemDetail";
40const char kOpenFileSystemDetailNonThrottledLabel[] =
41    "FileSystem.OpenFileSystemDetailNonthrottled";
42int64 kMinimumStatsCollectionIntervalHours = 1;
43
44// For type directory names in ObfuscatedFileUtil.
45// TODO(kinuko,nhiroki): Each type string registration should be done
46// via its own backend.
47const char kTemporaryDirectoryName[] = "t";
48const char kPersistentDirectoryName[] = "p";
49const char kSyncableDirectoryName[] = "s";
50
51const char* kPrepopulateTypes[] = {
52  kPersistentDirectoryName,
53  kTemporaryDirectoryName
54};
55
56enum FileSystemError {
57  kOK = 0,
58  kIncognito,
59  kInvalidSchemeError,
60  kCreateDirectoryError,
61  kNotFound,
62  kUnknownError,
63  kFileSystemErrorMax,
64};
65
66// Restricted names.
67// http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
68const base::FilePath::CharType* const kRestrictedNames[] = {
69  FILE_PATH_LITERAL("."), FILE_PATH_LITERAL(".."),
70};
71
72// Restricted chars.
73const base::FilePath::CharType kRestrictedChars[] = {
74  FILE_PATH_LITERAL('/'), FILE_PATH_LITERAL('\\'),
75};
76
77std::string GetTypeStringForURL(const FileSystemURL& url) {
78  return SandboxFileSystemBackendDelegate::GetTypeString(url.type());
79}
80
81std::set<std::string> GetKnownTypeStrings() {
82  std::set<std::string> known_type_strings;
83  known_type_strings.insert(kTemporaryDirectoryName);
84  known_type_strings.insert(kPersistentDirectoryName);
85  known_type_strings.insert(kSyncableDirectoryName);
86  return known_type_strings;
87}
88
89class ObfuscatedOriginEnumerator
90    : public SandboxFileSystemBackendDelegate::OriginEnumerator {
91 public:
92  explicit ObfuscatedOriginEnumerator(ObfuscatedFileUtil* file_util) {
93    enum_.reset(file_util->CreateOriginEnumerator());
94  }
95  virtual ~ObfuscatedOriginEnumerator() {}
96
97  virtual GURL Next() OVERRIDE {
98    return enum_->Next();
99  }
100
101  virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE {
102    return enum_->HasTypeDirectory(
103        SandboxFileSystemBackendDelegate::GetTypeString(type));
104  }
105
106 private:
107  scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enum_;
108};
109
110void OpenFileSystemOnFileTaskRunner(
111    ObfuscatedFileUtil* file_util,
112    const GURL& origin_url,
113    FileSystemType type,
114    OpenFileSystemMode mode,
115    base::File::Error* error_ptr) {
116  DCHECK(error_ptr);
117  const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
118  file_util->GetDirectoryForOriginAndType(
119      origin_url, SandboxFileSystemBackendDelegate::GetTypeString(type),
120      create, error_ptr);
121  if (*error_ptr != base::File::FILE_OK) {
122    UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel,
123                              kCreateDirectoryError,
124                              kFileSystemErrorMax);
125  } else {
126    UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kOK, kFileSystemErrorMax);
127  }
128  // The reference of file_util will be derefed on the FILE thread
129  // when the storage of this callback gets deleted regardless of whether
130  // this method is called or not.
131}
132
133void DidOpenFileSystem(
134    base::WeakPtr<SandboxFileSystemBackendDelegate> delegate,
135    const base::Callback<void(base::File::Error error)>& callback,
136    base::File::Error* error) {
137  if (delegate.get())
138    delegate.get()->CollectOpenFileSystemMetrics(*error);
139  callback.Run(*error);
140}
141
142template <typename T>
143void DeleteSoon(base::SequencedTaskRunner* runner, T* ptr) {
144  if (!runner->DeleteSoon(FROM_HERE, ptr))
145    delete ptr;
146}
147
148}  // namespace
149
150const base::FilePath::CharType
151SandboxFileSystemBackendDelegate::kFileSystemDirectory[] =
152    FILE_PATH_LITERAL("File System");
153
154// static
155std::string SandboxFileSystemBackendDelegate::GetTypeString(
156    FileSystemType type) {
157  switch (type) {
158    case kFileSystemTypeTemporary:
159      return kTemporaryDirectoryName;
160    case kFileSystemTypePersistent:
161      return kPersistentDirectoryName;
162    case kFileSystemTypeSyncable:
163    case kFileSystemTypeSyncableForInternalSync:
164      return kSyncableDirectoryName;
165    case kFileSystemTypeUnknown:
166    default:
167      NOTREACHED() << "Unknown filesystem type requested:" << type;
168      return std::string();
169  }
170}
171
172SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate(
173    quota::QuotaManagerProxy* quota_manager_proxy,
174    base::SequencedTaskRunner* file_task_runner,
175    const base::FilePath& profile_path,
176    quota::SpecialStoragePolicy* special_storage_policy,
177    const FileSystemOptions& file_system_options)
178    : file_task_runner_(file_task_runner),
179      sandbox_file_util_(new AsyncFileUtilAdapter(
180          new ObfuscatedFileUtil(
181              special_storage_policy,
182              profile_path.Append(kFileSystemDirectory),
183              file_system_options.env_override(),
184              file_task_runner,
185              base::Bind(&GetTypeStringForURL),
186              GetKnownTypeStrings(),
187              this))),
188      file_system_usage_cache_(new FileSystemUsageCache(file_task_runner)),
189      quota_observer_(new SandboxQuotaObserver(
190          quota_manager_proxy,
191          file_task_runner,
192          obfuscated_file_util(),
193          usage_cache())),
194      quota_reservation_manager_(new QuotaReservationManager(
195          scoped_ptr<QuotaReservationManager::QuotaBackend>(
196              new QuotaBackendImpl(file_task_runner_,
197                                   obfuscated_file_util(),
198                                   usage_cache(),
199                                   quota_manager_proxy)))),
200      special_storage_policy_(special_storage_policy),
201      file_system_options_(file_system_options),
202      is_filesystem_opened_(false),
203      weak_factory_(this) {
204  // Prepopulate database only if it can run asynchronously (i.e. the current
205  // thread is not file_task_runner). Usually this is the case but may not
206  // in test code.
207  if (!file_system_options.is_incognito() &&
208      !file_task_runner_->RunsTasksOnCurrentThread()) {
209    std::vector<std::string> types_to_prepopulate(
210        &kPrepopulateTypes[0],
211        &kPrepopulateTypes[arraysize(kPrepopulateTypes)]);
212    file_task_runner_->PostTask(
213        FROM_HERE,
214        base::Bind(&ObfuscatedFileUtil::MaybePrepopulateDatabase,
215                  base::Unretained(obfuscated_file_util()),
216                  types_to_prepopulate));
217  }
218}
219
220SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() {
221  io_thread_checker_.DetachFromThread();
222
223  if (!file_task_runner_->RunsTasksOnCurrentThread()) {
224    DeleteSoon(file_task_runner_.get(), quota_reservation_manager_.release());
225    DeleteSoon(file_task_runner_.get(), sandbox_file_util_.release());
226    DeleteSoon(file_task_runner_.get(), quota_observer_.release());
227    DeleteSoon(file_task_runner_.get(), file_system_usage_cache_.release());
228  }
229}
230
231SandboxFileSystemBackendDelegate::OriginEnumerator*
232SandboxFileSystemBackendDelegate::CreateOriginEnumerator() {
233  return new ObfuscatedOriginEnumerator(obfuscated_file_util());
234}
235
236base::FilePath
237SandboxFileSystemBackendDelegate::GetBaseDirectoryForOriginAndType(
238    const GURL& origin_url,
239    FileSystemType type,
240    bool create) {
241  base::File::Error error = base::File::FILE_OK;
242  base::FilePath path = obfuscated_file_util()->GetDirectoryForOriginAndType(
243      origin_url, GetTypeString(type), create, &error);
244  if (error != base::File::FILE_OK)
245    return base::FilePath();
246  return path;
247}
248
249void SandboxFileSystemBackendDelegate::OpenFileSystem(
250    const GURL& origin_url,
251    FileSystemType type,
252    OpenFileSystemMode mode,
253    const OpenFileSystemCallback& callback,
254    const GURL& root_url) {
255  if (!IsAllowedScheme(origin_url)) {
256    callback.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY);
257    return;
258  }
259
260  std::string name = GetFileSystemName(origin_url, type);
261
262  base::File::Error* error_ptr = new base::File::Error;
263  file_task_runner_->PostTaskAndReply(
264      FROM_HERE,
265      base::Bind(&OpenFileSystemOnFileTaskRunner,
266                 obfuscated_file_util(), origin_url, type, mode,
267                 base::Unretained(error_ptr)),
268      base::Bind(&DidOpenFileSystem,
269                 weak_factory_.GetWeakPtr(),
270                 base::Bind(callback, root_url, name),
271                 base::Owned(error_ptr)));
272
273  io_thread_checker_.DetachFromThread();
274  is_filesystem_opened_ = true;
275}
276
277scoped_ptr<FileSystemOperationContext>
278SandboxFileSystemBackendDelegate::CreateFileSystemOperationContext(
279    const FileSystemURL& url,
280    FileSystemContext* context,
281    base::File::Error* error_code) const {
282  if (!IsAccessValid(url)) {
283    *error_code = base::File::FILE_ERROR_SECURITY;
284    return scoped_ptr<FileSystemOperationContext>();
285  }
286
287  const UpdateObserverList* update_observers = GetUpdateObservers(url.type());
288  const ChangeObserverList* change_observers = GetChangeObservers(url.type());
289  DCHECK(update_observers);
290
291  scoped_ptr<FileSystemOperationContext> operation_context(
292      new FileSystemOperationContext(context));
293  operation_context->set_update_observers(*update_observers);
294  operation_context->set_change_observers(
295      change_observers ? *change_observers : ChangeObserverList());
296
297  return operation_context.Pass();
298}
299
300scoped_ptr<webkit_blob::FileStreamReader>
301SandboxFileSystemBackendDelegate::CreateFileStreamReader(
302    const FileSystemURL& url,
303    int64 offset,
304    const base::Time& expected_modification_time,
305    FileSystemContext* context) const {
306  if (!IsAccessValid(url))
307    return scoped_ptr<webkit_blob::FileStreamReader>();
308  return scoped_ptr<webkit_blob::FileStreamReader>(
309      webkit_blob::FileStreamReader::CreateForFileSystemFile(
310          context, url, offset, expected_modification_time));
311}
312
313scoped_ptr<FileStreamWriter>
314SandboxFileSystemBackendDelegate::CreateFileStreamWriter(
315    const FileSystemURL& url,
316    int64 offset,
317    FileSystemContext* context,
318    FileSystemType type) const {
319  if (!IsAccessValid(url))
320    return scoped_ptr<FileStreamWriter>();
321  const UpdateObserverList* observers = GetUpdateObservers(type);
322  DCHECK(observers);
323  return scoped_ptr<FileStreamWriter>(
324      new SandboxFileStreamWriter(context, url, offset, *observers));
325}
326
327base::File::Error
328SandboxFileSystemBackendDelegate::DeleteOriginDataOnFileTaskRunner(
329    FileSystemContext* file_system_context,
330    quota::QuotaManagerProxy* proxy,
331    const GURL& origin_url,
332    FileSystemType type) {
333  DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
334  int64 usage = GetOriginUsageOnFileTaskRunner(
335      file_system_context, origin_url, type);
336  usage_cache()->CloseCacheFiles();
337  bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType(
338      origin_url, GetTypeString(type));
339  if (result && proxy) {
340    proxy->NotifyStorageModified(
341        quota::QuotaClient::kFileSystem,
342        origin_url,
343        FileSystemTypeToQuotaStorageType(type),
344        -usage);
345  }
346
347  if (result)
348    return base::File::FILE_OK;
349  return base::File::FILE_ERROR_FAILED;
350}
351
352void SandboxFileSystemBackendDelegate::GetOriginsForTypeOnFileTaskRunner(
353    FileSystemType type, std::set<GURL>* origins) {
354  DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
355  DCHECK(origins);
356  scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
357  GURL origin;
358  while (!(origin = enumerator->Next()).is_empty()) {
359    if (enumerator->HasFileSystemType(type))
360      origins->insert(origin);
361  }
362  switch (type) {
363    case kFileSystemTypeTemporary:
364      UMA_HISTOGRAM_COUNTS(kTemporaryOriginsCountLabel, origins->size());
365      break;
366    case kFileSystemTypePersistent:
367      UMA_HISTOGRAM_COUNTS(kPersistentOriginsCountLabel, origins->size());
368      break;
369    default:
370      break;
371  }
372}
373
374void SandboxFileSystemBackendDelegate::GetOriginsForHostOnFileTaskRunner(
375    FileSystemType type, const std::string& host,
376    std::set<GURL>* origins) {
377  DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
378  DCHECK(origins);
379  scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
380  GURL origin;
381  while (!(origin = enumerator->Next()).is_empty()) {
382    if (host == net::GetHostOrSpecFromURL(origin) &&
383        enumerator->HasFileSystemType(type))
384      origins->insert(origin);
385  }
386}
387
388int64 SandboxFileSystemBackendDelegate::GetOriginUsageOnFileTaskRunner(
389    FileSystemContext* file_system_context,
390    const GURL& origin_url,
391    FileSystemType type) {
392  DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
393
394  // Don't use usage cache and return recalculated usage for sticky invalidated
395  // origins.
396  if (ContainsKey(sticky_dirty_origins_, std::make_pair(origin_url, type)))
397    return RecalculateUsage(file_system_context, origin_url, type);
398
399  base::FilePath base_path =
400      GetBaseDirectoryForOriginAndType(origin_url, type, false);
401  if (base_path.empty() || !base::DirectoryExists(base_path))
402    return 0;
403  base::FilePath usage_file_path =
404      base_path.Append(FileSystemUsageCache::kUsageFileName);
405
406  bool is_valid = usage_cache()->IsValid(usage_file_path);
407  uint32 dirty_status = 0;
408  bool dirty_status_available =
409      usage_cache()->GetDirty(usage_file_path, &dirty_status);
410  bool visited = !visited_origins_.insert(origin_url).second;
411  if (is_valid && (dirty_status == 0 || (dirty_status_available && visited))) {
412    // The usage cache is clean (dirty == 0) or the origin is already
413    // initialized and running.  Read the cache file to get the usage.
414    int64 usage = 0;
415    return usage_cache()->GetUsage(usage_file_path, &usage) ? usage : -1;
416  }
417  // The usage cache has not been initialized or the cache is dirty.
418  // Get the directory size now and update the cache.
419  usage_cache()->Delete(usage_file_path);
420
421  int64 usage = RecalculateUsage(file_system_context, origin_url, type);
422
423  // This clears the dirty flag too.
424  usage_cache()->UpdateUsage(usage_file_path, usage);
425  return usage;
426}
427
428scoped_refptr<QuotaReservation>
429SandboxFileSystemBackendDelegate::CreateQuotaReservationOnFileTaskRunner(
430    const GURL& origin,
431    FileSystemType type) {
432  DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
433  DCHECK(quota_reservation_manager_);
434  return quota_reservation_manager_->CreateReservation(origin, type);
435}
436
437void SandboxFileSystemBackendDelegate::AddFileUpdateObserver(
438    FileSystemType type,
439    FileUpdateObserver* observer,
440    base::SequencedTaskRunner* task_runner) {
441  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
442  update_observers_[type] =
443      update_observers_[type].AddObserver(observer, task_runner);
444}
445
446void SandboxFileSystemBackendDelegate::AddFileChangeObserver(
447    FileSystemType type,
448    FileChangeObserver* observer,
449    base::SequencedTaskRunner* task_runner) {
450  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
451  change_observers_[type] =
452      change_observers_[type].AddObserver(observer, task_runner);
453}
454
455void SandboxFileSystemBackendDelegate::AddFileAccessObserver(
456    FileSystemType type,
457    FileAccessObserver* observer,
458    base::SequencedTaskRunner* task_runner) {
459  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
460  access_observers_[type] =
461      access_observers_[type].AddObserver(observer, task_runner);
462}
463
464const UpdateObserverList* SandboxFileSystemBackendDelegate::GetUpdateObservers(
465    FileSystemType type) const {
466  std::map<FileSystemType, UpdateObserverList>::const_iterator iter =
467      update_observers_.find(type);
468  if (iter == update_observers_.end())
469    return NULL;
470  return &iter->second;
471}
472
473const ChangeObserverList* SandboxFileSystemBackendDelegate::GetChangeObservers(
474    FileSystemType type) const {
475  std::map<FileSystemType, ChangeObserverList>::const_iterator iter =
476      change_observers_.find(type);
477  if (iter == change_observers_.end())
478    return NULL;
479  return &iter->second;
480}
481
482const AccessObserverList* SandboxFileSystemBackendDelegate::GetAccessObservers(
483    FileSystemType type) const {
484  std::map<FileSystemType, AccessObserverList>::const_iterator iter =
485      access_observers_.find(type);
486  if (iter == access_observers_.end())
487    return NULL;
488  return &iter->second;
489}
490
491void SandboxFileSystemBackendDelegate::RegisterQuotaUpdateObserver(
492    FileSystemType type) {
493  AddFileUpdateObserver(type, quota_observer_.get(), file_task_runner_.get());
494}
495
496void SandboxFileSystemBackendDelegate::InvalidateUsageCache(
497    const GURL& origin,
498    FileSystemType type) {
499  base::File::Error error = base::File::FILE_OK;
500  base::FilePath usage_file_path = GetUsageCachePathForOriginAndType(
501      obfuscated_file_util(), origin, type, &error);
502  if (error != base::File::FILE_OK)
503    return;
504  usage_cache()->IncrementDirty(usage_file_path);
505}
506
507void SandboxFileSystemBackendDelegate::StickyInvalidateUsageCache(
508    const GURL& origin,
509    FileSystemType type) {
510  sticky_dirty_origins_.insert(std::make_pair(origin, type));
511  quota_observer()->SetUsageCacheEnabled(origin, type, false);
512  InvalidateUsageCache(origin, type);
513}
514
515FileSystemFileUtil* SandboxFileSystemBackendDelegate::sync_file_util() {
516  return static_cast<AsyncFileUtilAdapter*>(file_util())->sync_file_util();
517}
518
519bool SandboxFileSystemBackendDelegate::IsAccessValid(
520    const FileSystemURL& url) const {
521  if (!IsAllowedScheme(url.origin()))
522    return false;
523
524  if (url.path().ReferencesParent())
525    return false;
526
527  // Return earlier if the path is '/', because VirtualPath::BaseName()
528  // returns '/' for '/' and we fail the "basename != '/'" check below.
529  // (We exclude '.' because it's disallowed by spec.)
530  if (VirtualPath::IsRootPath(url.path()) &&
531      url.path() != base::FilePath(base::FilePath::kCurrentDirectory))
532    return true;
533
534  // Restricted names specified in
535  // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
536  base::FilePath filename = VirtualPath::BaseName(url.path());
537  // See if the name is allowed to create.
538  for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) {
539    if (filename.value() == kRestrictedNames[i])
540      return false;
541  }
542  for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) {
543    if (filename.value().find(kRestrictedChars[i]) !=
544        base::FilePath::StringType::npos)
545      return false;
546  }
547
548  return true;
549}
550
551bool SandboxFileSystemBackendDelegate::IsAllowedScheme(const GURL& url) const {
552  // Basically we only accept http or https. We allow file:// URLs
553  // only if --allow-file-access-from-files flag is given.
554  if (url.SchemeIsHTTPOrHTTPS())
555    return true;
556  if (url.SchemeIsFileSystem())
557    return url.inner_url() && IsAllowedScheme(*url.inner_url());
558
559  for (size_t i = 0;
560       i < file_system_options_.additional_allowed_schemes().size();
561       ++i) {
562    if (url.SchemeIs(
563            file_system_options_.additional_allowed_schemes()[i].c_str()))
564      return true;
565  }
566  return false;
567}
568
569base::FilePath
570SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
571    const GURL& origin_url,
572    FileSystemType type) {
573  base::File::Error error;
574  base::FilePath path = GetUsageCachePathForOriginAndType(
575      obfuscated_file_util(), origin_url, type, &error);
576  if (error != base::File::FILE_OK)
577    return base::FilePath();
578  return path;
579}
580
581// static
582base::FilePath
583SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
584    ObfuscatedFileUtil* sandbox_file_util,
585    const GURL& origin_url,
586    FileSystemType type,
587    base::File::Error* error_out) {
588  DCHECK(error_out);
589  *error_out = base::File::FILE_OK;
590  base::FilePath base_path = sandbox_file_util->GetDirectoryForOriginAndType(
591      origin_url, GetTypeString(type), false /* create */, error_out);
592  if (*error_out != base::File::FILE_OK)
593    return base::FilePath();
594  return base_path.Append(FileSystemUsageCache::kUsageFileName);
595}
596
597int64 SandboxFileSystemBackendDelegate::RecalculateUsage(
598    FileSystemContext* context,
599    const GURL& origin,
600    FileSystemType type) {
601  FileSystemOperationContext operation_context(context);
602  FileSystemURL url = context->CreateCrackedFileSystemURL(
603      origin, type, base::FilePath());
604  scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
605      obfuscated_file_util()->CreateFileEnumerator(
606          &operation_context, url, true));
607
608  base::FilePath file_path_each;
609  int64 usage = 0;
610
611  while (!(file_path_each = enumerator->Next()).empty()) {
612    usage += enumerator->Size();
613    usage += ObfuscatedFileUtil::ComputeFilePathCost(file_path_each);
614  }
615
616  return usage;
617}
618
619void SandboxFileSystemBackendDelegate::CollectOpenFileSystemMetrics(
620    base::File::Error error_code) {
621  base::Time now = base::Time::Now();
622  bool throttled = now < next_release_time_for_open_filesystem_stat_;
623  if (!throttled) {
624    next_release_time_for_open_filesystem_stat_ =
625        now + base::TimeDelta::FromHours(kMinimumStatsCollectionIntervalHours);
626  }
627
628#define REPORT(report_value)                                            \
629  UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailLabel,                 \
630                            (report_value),                             \
631                            kFileSystemErrorMax);                       \
632  if (!throttled) {                                                     \
633    UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailNonThrottledLabel,   \
634                              (report_value),                           \
635                              kFileSystemErrorMax);                     \
636  }
637
638  switch (error_code) {
639    case base::File::FILE_OK:
640      REPORT(kOK);
641      break;
642    case base::File::FILE_ERROR_INVALID_URL:
643      REPORT(kInvalidSchemeError);
644      break;
645    case base::File::FILE_ERROR_NOT_FOUND:
646      REPORT(kNotFound);
647      break;
648    case base::File::FILE_ERROR_FAILED:
649    default:
650      REPORT(kUnknownError);
651      break;
652  }
653#undef REPORT
654}
655
656ObfuscatedFileUtil* SandboxFileSystemBackendDelegate::obfuscated_file_util() {
657  return static_cast<ObfuscatedFileUtil*>(sync_file_util());
658}
659
660// Declared in obfuscated_file_util.h.
661// static
662ObfuscatedFileUtil* ObfuscatedFileUtil::CreateForTesting(
663    quota::SpecialStoragePolicy* special_storage_policy,
664    const base::FilePath& file_system_directory,
665    leveldb::Env* env_override,
666    base::SequencedTaskRunner* file_task_runner) {
667  return new ObfuscatedFileUtil(special_storage_policy,
668                                file_system_directory,
669                                env_override,
670                                file_task_runner,
671                                base::Bind(&GetTypeStringForURL),
672                                GetKnownTypeStrings(),
673                                NULL);
674}
675
676}  // namespace fileapi
677