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.
5 #include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/task_runner_util.h"
12 #include "net/base/net_util.h"
13 #include "webkit/browser/blob/file_stream_reader.h"
14 #include "webkit/browser/fileapi/async_file_util_adapter.h"
15 #include "webkit/browser/fileapi/file_system_context.h"
16 #include "webkit/browser/fileapi/file_system_operation_context.h"
17 #include "webkit/browser/fileapi/file_system_url.h"
18 #include "webkit/browser/fileapi/file_system_usage_cache.h"
19 #include "webkit/browser/fileapi/obfuscated_file_util.h"
20 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
21 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
22 #include "webkit/browser/fileapi/sandbox_quota_observer.h"
23 #include "webkit/browser/quota/quota_manager.h"
24 #include "webkit/common/fileapi/file_system_util.h"
30 const char kTemporaryOriginsCountLabel[] = "FileSystem.TemporaryOriginsCount";
31 const char kPersistentOriginsCountLabel[] = "FileSystem.PersistentOriginsCount";
33 const char kOpenFileSystemLabel[] = "FileSystem.OpenFileSystem";
34 const char kOpenFileSystemDetailLabel[] = "FileSystem.OpenFileSystemDetail";
35 const char kOpenFileSystemDetailNonThrottledLabel[] =
36 "FileSystem.OpenFileSystemDetailNonthrottled";
37 int64 kMinimumStatsCollectionIntervalHours = 1;
39 // For type directory names in ObfuscatedFileUtil.
40 // TODO(kinuko,nhiroki): Each type string registration should be done
41 // via its own backend.
42 const char kTemporaryDirectoryName[] = "t";
43 const char kPersistentDirectoryName[] = "p";
44 const char kSyncableDirectoryName[] = "s";
46 const char* kPrepopulateTypes[] = {
47 kPersistentDirectoryName,
48 kTemporaryDirectoryName
51 enum FileSystemError {
55 kCreateDirectoryError,
62 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
63 const base::FilePath::CharType* const kRestrictedNames[] = {
64 FILE_PATH_LITERAL("."), FILE_PATH_LITERAL(".."),
68 const base::FilePath::CharType kRestrictedChars[] = {
69 FILE_PATH_LITERAL('/'), FILE_PATH_LITERAL('\\'),
72 std::string GetTypeStringForURL(const FileSystemURL& url) {
73 return SandboxFileSystemBackendDelegate::GetTypeString(url.type());
76 std::set<std::string> GetKnownTypeStrings() {
77 std::set<std::string> known_type_strings;
78 known_type_strings.insert(kTemporaryDirectoryName);
79 known_type_strings.insert(kPersistentDirectoryName);
80 known_type_strings.insert(kSyncableDirectoryName);
81 return known_type_strings;
84 class ObfuscatedOriginEnumerator
85 : public SandboxFileSystemBackendDelegate::OriginEnumerator {
87 explicit ObfuscatedOriginEnumerator(ObfuscatedFileUtil* file_util) {
88 enum_.reset(file_util->CreateOriginEnumerator());
90 virtual ~ObfuscatedOriginEnumerator() {}
92 virtual GURL Next() OVERRIDE {
96 virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE {
97 return enum_->HasTypeDirectory(
98 SandboxFileSystemBackendDelegate::GetTypeString(type));
102 scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enum_;
105 void OpenFileSystemOnFileThread(
106 ObfuscatedFileUtil* file_util,
107 const GURL& origin_url,
109 OpenFileSystemMode mode,
110 base::PlatformFileError* error_ptr) {
112 const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
113 file_util->GetDirectoryForOriginAndType(
114 origin_url, SandboxFileSystemBackendDelegate::GetTypeString(type),
116 if (*error_ptr != base::PLATFORM_FILE_OK) {
117 UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel,
118 kCreateDirectoryError,
119 kFileSystemErrorMax);
121 UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kOK, kFileSystemErrorMax);
123 // The reference of file_util will be derefed on the FILE thread
124 // when the storage of this callback gets deleted regardless of whether
125 // this method is called or not.
128 void DidOpenFileSystem(
129 base::WeakPtr<SandboxFileSystemBackendDelegate> delegate,
130 const base::Callback<void(base::PlatformFileError error)>& callback,
131 base::PlatformFileError* error) {
133 delegate.get()->CollectOpenFileSystemMetrics(*error);
134 callback.Run(*error);
137 template <typename T>
138 void DeleteSoon(base::SequencedTaskRunner* runner, T* ptr) {
139 if (!runner->DeleteSoon(FROM_HERE, ptr))
145 const base::FilePath::CharType
146 SandboxFileSystemBackendDelegate::kFileSystemDirectory[] =
147 FILE_PATH_LITERAL("File System");
150 std::string SandboxFileSystemBackendDelegate::GetTypeString(
151 FileSystemType type) {
153 case kFileSystemTypeTemporary:
154 return kTemporaryDirectoryName;
155 case kFileSystemTypePersistent:
156 return kPersistentDirectoryName;
157 case kFileSystemTypeSyncable:
158 case kFileSystemTypeSyncableForInternalSync:
159 return kSyncableDirectoryName;
160 case kFileSystemTypeUnknown:
162 NOTREACHED() << "Unknown filesystem type requested:" << type;
163 return std::string();
167 SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate(
168 quota::QuotaManagerProxy* quota_manager_proxy,
169 base::SequencedTaskRunner* file_task_runner,
170 const base::FilePath& profile_path,
171 quota::SpecialStoragePolicy* special_storage_policy,
172 const FileSystemOptions& file_system_options)
173 : file_task_runner_(file_task_runner),
174 sandbox_file_util_(new AsyncFileUtilAdapter(
175 new ObfuscatedFileUtil(
176 special_storage_policy,
177 profile_path.Append(kFileSystemDirectory),
179 base::Bind(&GetTypeStringForURL),
180 GetKnownTypeStrings(),
182 file_system_usage_cache_(new FileSystemUsageCache(file_task_runner)),
183 quota_observer_(new SandboxQuotaObserver(
186 obfuscated_file_util(),
188 special_storage_policy_(special_storage_policy),
189 file_system_options_(file_system_options),
190 is_filesystem_opened_(false),
191 weak_factory_(this) {
192 // Prepopulate database only if it can run asynchronously (i.e. the current
193 // thread is not file_task_runner). Usually this is the case but may not
195 if (!file_task_runner_->RunsTasksOnCurrentThread()) {
196 std::vector<std::string> types_to_prepopulate(
197 &kPrepopulateTypes[0],
198 &kPrepopulateTypes[arraysize(kPrepopulateTypes)]);
199 file_task_runner_->PostTask(
201 base::Bind(&ObfuscatedFileUtil::MaybePrepopulateDatabase,
202 base::Unretained(obfuscated_file_util()),
203 types_to_prepopulate));
207 SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() {
208 io_thread_checker_.DetachFromThread();
210 if (!file_task_runner_->RunsTasksOnCurrentThread()) {
211 DeleteSoon(file_task_runner_.get(), sandbox_file_util_.release());
212 DeleteSoon(file_task_runner_.get(), quota_observer_.release());
213 DeleteSoon(file_task_runner_.get(), file_system_usage_cache_.release());
217 SandboxFileSystemBackendDelegate::OriginEnumerator*
218 SandboxFileSystemBackendDelegate::CreateOriginEnumerator() {
219 return new ObfuscatedOriginEnumerator(obfuscated_file_util());
223 SandboxFileSystemBackendDelegate::GetBaseDirectoryForOriginAndType(
224 const GURL& origin_url,
227 base::PlatformFileError error = base::PLATFORM_FILE_OK;
228 base::FilePath path = obfuscated_file_util()->GetDirectoryForOriginAndType(
229 origin_url, GetTypeString(type), create, &error);
230 if (error != base::PLATFORM_FILE_OK)
231 return base::FilePath();
235 void SandboxFileSystemBackendDelegate::OpenFileSystem(
236 const GURL& origin_url,
238 OpenFileSystemMode mode,
239 const OpenFileSystemCallback& callback,
240 const GURL& root_url) {
241 if (!IsAllowedScheme(origin_url)) {
242 callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY);
246 std::string name = GetFileSystemName(origin_url, type);
248 base::PlatformFileError* error_ptr = new base::PlatformFileError;
249 file_task_runner_->PostTaskAndReply(
251 base::Bind(&OpenFileSystemOnFileThread,
252 obfuscated_file_util(), origin_url, type, mode,
253 base::Unretained(error_ptr)),
254 base::Bind(&DidOpenFileSystem,
255 weak_factory_.GetWeakPtr(),
256 base::Bind(callback, root_url, name),
257 base::Owned(error_ptr)));
259 io_thread_checker_.DetachFromThread();
260 is_filesystem_opened_ = true;
263 scoped_ptr<FileSystemOperationContext>
264 SandboxFileSystemBackendDelegate::CreateFileSystemOperationContext(
265 const FileSystemURL& url,
266 FileSystemContext* context,
267 base::PlatformFileError* error_code) const {
268 if (!IsAccessValid(url)) {
269 *error_code = base::PLATFORM_FILE_ERROR_SECURITY;
270 return scoped_ptr<FileSystemOperationContext>();
273 const UpdateObserverList* update_observers = GetUpdateObservers(url.type());
274 const ChangeObserverList* change_observers = GetChangeObservers(url.type());
275 DCHECK(update_observers);
277 scoped_ptr<FileSystemOperationContext> operation_context(
278 new FileSystemOperationContext(context));
279 operation_context->set_update_observers(*update_observers);
280 operation_context->set_change_observers(
281 change_observers ? *change_observers : ChangeObserverList());
283 return operation_context.Pass();
286 scoped_ptr<webkit_blob::FileStreamReader>
287 SandboxFileSystemBackendDelegate::CreateFileStreamReader(
288 const FileSystemURL& url,
290 const base::Time& expected_modification_time,
291 FileSystemContext* context) const {
292 if (!IsAccessValid(url))
293 return scoped_ptr<webkit_blob::FileStreamReader>();
294 return scoped_ptr<webkit_blob::FileStreamReader>(
295 webkit_blob::FileStreamReader::CreateForFileSystemFile(
296 context, url, offset, expected_modification_time));
299 scoped_ptr<FileStreamWriter>
300 SandboxFileSystemBackendDelegate::CreateFileStreamWriter(
301 const FileSystemURL& url,
303 FileSystemContext* context,
304 FileSystemType type) const {
305 if (!IsAccessValid(url))
306 return scoped_ptr<FileStreamWriter>();
307 const UpdateObserverList* observers = GetUpdateObservers(type);
309 return scoped_ptr<FileStreamWriter>(
310 new SandboxFileStreamWriter(context, url, offset, *observers));
313 base::PlatformFileError
314 SandboxFileSystemBackendDelegate::DeleteOriginDataOnFileThread(
315 FileSystemContext* file_system_context,
316 quota::QuotaManagerProxy* proxy,
317 const GURL& origin_url,
318 FileSystemType type) {
319 int64 usage = GetOriginUsageOnFileThread(
320 file_system_context, origin_url, type);
321 usage_cache()->CloseCacheFiles();
322 bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType(
323 origin_url, GetTypeString(type));
324 if (result && proxy) {
325 proxy->NotifyStorageModified(
326 quota::QuotaClient::kFileSystem,
328 FileSystemTypeToQuotaStorageType(type),
333 return base::PLATFORM_FILE_OK;
334 return base::PLATFORM_FILE_ERROR_FAILED;
337 void SandboxFileSystemBackendDelegate::GetOriginsForTypeOnFileThread(
338 FileSystemType type, std::set<GURL>* origins) {
340 scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
342 while (!(origin = enumerator->Next()).is_empty()) {
343 if (enumerator->HasFileSystemType(type))
344 origins->insert(origin);
347 case kFileSystemTypeTemporary:
348 UMA_HISTOGRAM_COUNTS(kTemporaryOriginsCountLabel, origins->size());
350 case kFileSystemTypePersistent:
351 UMA_HISTOGRAM_COUNTS(kPersistentOriginsCountLabel, origins->size());
358 void SandboxFileSystemBackendDelegate::GetOriginsForHostOnFileThread(
359 FileSystemType type, const std::string& host,
360 std::set<GURL>* origins) {
362 scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
364 while (!(origin = enumerator->Next()).is_empty()) {
365 if (host == net::GetHostOrSpecFromURL(origin) &&
366 enumerator->HasFileSystemType(type))
367 origins->insert(origin);
371 int64 SandboxFileSystemBackendDelegate::GetOriginUsageOnFileThread(
372 FileSystemContext* file_system_context,
373 const GURL& origin_url,
374 FileSystemType type) {
375 // Don't use usage cache and return recalculated usage for sticky invalidated
377 if (ContainsKey(sticky_dirty_origins_, std::make_pair(origin_url, type)))
378 return RecalculateUsage(file_system_context, origin_url, type);
380 base::FilePath base_path =
381 GetBaseDirectoryForOriginAndType(origin_url, type, false);
382 if (base_path.empty() || !base::DirectoryExists(base_path))
384 base::FilePath usage_file_path =
385 base_path.Append(FileSystemUsageCache::kUsageFileName);
387 bool is_valid = usage_cache()->IsValid(usage_file_path);
388 uint32 dirty_status = 0;
389 bool dirty_status_available =
390 usage_cache()->GetDirty(usage_file_path, &dirty_status);
391 bool visited = !visited_origins_.insert(origin_url).second;
392 if (is_valid && (dirty_status == 0 || (dirty_status_available && visited))) {
393 // The usage cache is clean (dirty == 0) or the origin is already
394 // initialized and running. Read the cache file to get the usage.
396 return usage_cache()->GetUsage(usage_file_path, &usage) ? usage : -1;
398 // The usage cache has not been initialized or the cache is dirty.
399 // Get the directory size now and update the cache.
400 usage_cache()->Delete(usage_file_path);
402 int64 usage = RecalculateUsage(file_system_context, origin_url, type);
404 // This clears the dirty flag too.
405 usage_cache()->UpdateUsage(usage_file_path, usage);
409 void SandboxFileSystemBackendDelegate::AddFileUpdateObserver(
411 FileUpdateObserver* observer,
412 base::SequencedTaskRunner* task_runner) {
413 DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
414 update_observers_[type] =
415 update_observers_[type].AddObserver(observer, task_runner);
418 void SandboxFileSystemBackendDelegate::AddFileChangeObserver(
420 FileChangeObserver* observer,
421 base::SequencedTaskRunner* task_runner) {
422 DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
423 change_observers_[type] =
424 change_observers_[type].AddObserver(observer, task_runner);
427 void SandboxFileSystemBackendDelegate::AddFileAccessObserver(
429 FileAccessObserver* observer,
430 base::SequencedTaskRunner* task_runner) {
431 DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
432 access_observers_[type] =
433 access_observers_[type].AddObserver(observer, task_runner);
436 const UpdateObserverList* SandboxFileSystemBackendDelegate::GetUpdateObservers(
437 FileSystemType type) const {
438 std::map<FileSystemType, UpdateObserverList>::const_iterator iter =
439 update_observers_.find(type);
440 if (iter == update_observers_.end())
442 return &iter->second;
445 const ChangeObserverList* SandboxFileSystemBackendDelegate::GetChangeObservers(
446 FileSystemType type) const {
447 std::map<FileSystemType, ChangeObserverList>::const_iterator iter =
448 change_observers_.find(type);
449 if (iter == change_observers_.end())
451 return &iter->second;
454 const AccessObserverList* SandboxFileSystemBackendDelegate::GetAccessObservers(
455 FileSystemType type) const {
456 std::map<FileSystemType, AccessObserverList>::const_iterator iter =
457 access_observers_.find(type);
458 if (iter == access_observers_.end())
460 return &iter->second;
463 void SandboxFileSystemBackendDelegate::RegisterQuotaUpdateObserver(
464 FileSystemType type) {
465 AddFileUpdateObserver(type, quota_observer_.get(), file_task_runner_.get());
468 void SandboxFileSystemBackendDelegate::InvalidateUsageCache(
470 FileSystemType type) {
471 base::PlatformFileError error = base::PLATFORM_FILE_OK;
472 base::FilePath usage_file_path = GetUsageCachePathForOriginAndType(
473 obfuscated_file_util(), origin, type, &error);
474 if (error != base::PLATFORM_FILE_OK)
476 usage_cache()->IncrementDirty(usage_file_path);
479 void SandboxFileSystemBackendDelegate::StickyInvalidateUsageCache(
481 FileSystemType type) {
482 sticky_dirty_origins_.insert(std::make_pair(origin, type));
483 quota_observer()->SetUsageCacheEnabled(origin, type, false);
484 InvalidateUsageCache(origin, type);
487 FileSystemFileUtil* SandboxFileSystemBackendDelegate::sync_file_util() {
488 return static_cast<AsyncFileUtilAdapter*>(file_util())->sync_file_util();
491 bool SandboxFileSystemBackendDelegate::IsAccessValid(
492 const FileSystemURL& url) const {
493 if (!IsAllowedScheme(url.origin()))
496 if (url.path().ReferencesParent())
499 // Return earlier if the path is '/', because VirtualPath::BaseName()
500 // returns '/' for '/' and we fail the "basename != '/'" check below.
501 // (We exclude '.' because it's disallowed by spec.)
502 if (VirtualPath::IsRootPath(url.path()) &&
503 url.path() != base::FilePath(base::FilePath::kCurrentDirectory))
506 // Restricted names specified in
507 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
508 base::FilePath filename = VirtualPath::BaseName(url.path());
509 // See if the name is allowed to create.
510 for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) {
511 if (filename.value() == kRestrictedNames[i])
514 for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) {
515 if (filename.value().find(kRestrictedChars[i]) !=
516 base::FilePath::StringType::npos)
523 bool SandboxFileSystemBackendDelegate::IsAllowedScheme(const GURL& url) const {
524 // Basically we only accept http or https. We allow file:// URLs
525 // only if --allow-file-access-from-files flag is given.
526 if (url.SchemeIsHTTPOrHTTPS())
528 if (url.SchemeIsFileSystem())
529 return url.inner_url() && IsAllowedScheme(*url.inner_url());
532 i < file_system_options_.additional_allowed_schemes().size();
535 file_system_options_.additional_allowed_schemes()[i].c_str()))
542 SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
543 const GURL& origin_url,
544 FileSystemType type) {
545 base::PlatformFileError error;
546 base::FilePath path = GetUsageCachePathForOriginAndType(
547 obfuscated_file_util(), origin_url, type, &error);
548 if (error != base::PLATFORM_FILE_OK)
549 return base::FilePath();
555 SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
556 ObfuscatedFileUtil* sandbox_file_util,
557 const GURL& origin_url,
559 base::PlatformFileError* error_out) {
561 *error_out = base::PLATFORM_FILE_OK;
562 base::FilePath base_path = sandbox_file_util->GetDirectoryForOriginAndType(
563 origin_url, GetTypeString(type), false /* create */, error_out);
564 if (*error_out != base::PLATFORM_FILE_OK)
565 return base::FilePath();
566 return base_path.Append(FileSystemUsageCache::kUsageFileName);
569 int64 SandboxFileSystemBackendDelegate::RecalculateUsage(
570 FileSystemContext* context,
572 FileSystemType type) {
573 FileSystemOperationContext operation_context(context);
574 FileSystemURL url = context->CreateCrackedFileSystemURL(
575 origin, type, base::FilePath());
576 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
577 obfuscated_file_util()->CreateFileEnumerator(
578 &operation_context, url, true));
580 base::FilePath file_path_each;
583 while (!(file_path_each = enumerator->Next()).empty()) {
584 usage += enumerator->Size();
585 usage += ObfuscatedFileUtil::ComputeFilePathCost(file_path_each);
591 void SandboxFileSystemBackendDelegate::CollectOpenFileSystemMetrics(
592 base::PlatformFileError error_code) {
593 base::Time now = base::Time::Now();
594 bool throttled = now < next_release_time_for_open_filesystem_stat_;
596 next_release_time_for_open_filesystem_stat_ =
597 now + base::TimeDelta::FromHours(kMinimumStatsCollectionIntervalHours);
600 #define REPORT(report_value) \
601 UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailLabel, \
603 kFileSystemErrorMax); \
605 UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailNonThrottledLabel, \
607 kFileSystemErrorMax); \
610 switch (error_code) {
611 case base::PLATFORM_FILE_OK:
614 case base::PLATFORM_FILE_ERROR_INVALID_URL:
615 REPORT(kInvalidSchemeError);
617 case base::PLATFORM_FILE_ERROR_NOT_FOUND:
620 case base::PLATFORM_FILE_ERROR_FAILED:
622 REPORT(kUnknownError);
628 ObfuscatedFileUtil* SandboxFileSystemBackendDelegate::obfuscated_file_util() {
629 return static_cast<ObfuscatedFileUtil*>(sync_file_util());
632 // Declared in obfuscated_file_util.h.
634 ObfuscatedFileUtil* ObfuscatedFileUtil::CreateForTesting(
635 quota::SpecialStoragePolicy* special_storage_policy,
636 const base::FilePath& file_system_directory,
637 base::SequencedTaskRunner* file_task_runner) {
638 return new ObfuscatedFileUtil(special_storage_policy,
639 file_system_directory,
641 base::Bind(&GetTypeStringForURL),
642 GetKnownTypeStrings(),
646 } // namespace fileapi