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.
5 #include "webkit/browser/fileapi/obfuscated_file_util.h"
11 #include "base/file_util.h"
12 #include "base/format_macros.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
23 #include "webkit/browser/fileapi/file_observers.h"
24 #include "webkit/browser/fileapi/file_system_context.h"
25 #include "webkit/browser/fileapi/file_system_operation_context.h"
26 #include "webkit/browser/fileapi/file_system_url.h"
27 #include "webkit/browser/fileapi/native_file_util.h"
28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
29 #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
30 #include "webkit/browser/fileapi/sandbox_origin_database.h"
31 #include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h"
32 #include "webkit/browser/fileapi/timed_task_helper.h"
33 #include "webkit/browser/quota/quota_manager.h"
34 #include "webkit/common/database/database_identifier.h"
35 #include "webkit/common/fileapi/file_system_util.h"
37 // Example of various paths:
38 // void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
39 // base::FilePath virtual_path = url.path();
40 // base::FilePath local_path = GetLocalFilePath(url);
42 // NativeFileUtil::DoSomething(local_path);
43 // file_util::DoAnother(local_path);
50 typedef SandboxDirectoryDatabase::FileId FileId
;
51 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
54 SandboxDirectoryDatabase::FileInfo
* file_info
,
55 SandboxDirectoryDatabase::FileId parent_id
,
56 const base::FilePath::StringType
& file_name
) {
58 file_info
->parent_id
= parent_id
;
59 file_info
->name
= file_name
;
62 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of
63 // path storage under Linux. It's not clear if that will differ on Windows, on
64 // which base::FilePath uses wide chars [since they're converted to UTF-8 for
65 // storage anyway], but as long as the cost is high enough that one can't cheat
66 // on quota by storing data in paths, it doesn't need to be all that accurate.
67 const int64 kPathCreationQuotaCost
= 146; // Bytes per inode, basically.
68 const int64 kPathByteQuotaCost
= 2; // Bytes per byte of path length in UTF-8.
70 int64
UsageForPath(size_t length
) {
71 return kPathCreationQuotaCost
+
72 static_cast<int64
>(length
) * kPathByteQuotaCost
;
75 bool AllocateQuota(FileSystemOperationContext
* context
, int64 growth
) {
76 if (context
->allowed_bytes_growth() == quota::QuotaManager::kNoLimit
)
79 int64 new_quota
= context
->allowed_bytes_growth() - growth
;
80 if (growth
> 0 && new_quota
< 0)
82 context
->set_allowed_bytes_growth(new_quota
);
87 FileSystemOperationContext
* context
,
88 const FileSystemURL
& url
,
90 context
->update_observers()->Notify(
91 &FileUpdateObserver::OnUpdate
, MakeTuple(url
, growth
));
94 void TouchDirectory(SandboxDirectoryDatabase
* db
, FileId dir_id
) {
96 if (!db
->UpdateModificationTime(dir_id
, base::Time::Now()))
100 enum IsolatedOriginStatus
{
101 kIsolatedOriginMatch
,
102 kIsolatedOriginDontMatch
,
103 kIsolatedOriginStatusMax
,
108 using base::PlatformFile
;
109 using base::PlatformFileError
;
111 class ObfuscatedFileEnumerator
112 : public FileSystemFileUtil::AbstractFileEnumerator
{
114 ObfuscatedFileEnumerator(
115 SandboxDirectoryDatabase
* db
,
116 FileSystemOperationContext
* context
,
117 ObfuscatedFileUtil
* obfuscated_file_util
,
118 const FileSystemURL
& root_url
,
122 obfuscated_file_util_(obfuscated_file_util
),
124 recursive_(recursive
),
125 current_file_id_(0) {
126 base::FilePath root_virtual_path
= root_url
.path();
129 if (!db_
->GetFileWithPath(root_virtual_path
, &file_id
))
132 FileRecord record
= { file_id
, root_virtual_path
};
133 recurse_queue_
.push(record
);
136 virtual ~ObfuscatedFileEnumerator() {}
138 virtual base::FilePath
Next() OVERRIDE
{
139 ProcessRecurseQueue();
140 if (display_stack_
.empty())
141 return base::FilePath();
143 current_file_id_
= display_stack_
.back();
144 display_stack_
.pop_back();
147 base::FilePath platform_file_path
;
148 base::PlatformFileError error
=
149 obfuscated_file_util_
->GetFileInfoInternal(
150 db_
, context_
, root_url_
, current_file_id_
,
151 &file_info
, ¤t_platform_file_info_
, &platform_file_path
);
152 if (error
!= base::PLATFORM_FILE_OK
)
155 base::FilePath virtual_path
=
156 current_parent_virtual_path_
.Append(file_info
.name
);
157 if (recursive_
&& file_info
.is_directory()) {
158 FileRecord record
= { current_file_id_
, virtual_path
};
159 recurse_queue_
.push(record
);
164 virtual int64
Size() OVERRIDE
{
165 return current_platform_file_info_
.size
;
168 virtual base::Time
LastModifiedTime() OVERRIDE
{
169 return current_platform_file_info_
.last_modified
;
172 virtual bool IsDirectory() OVERRIDE
{
173 return current_platform_file_info_
.is_directory
;
177 typedef SandboxDirectoryDatabase::FileId FileId
;
178 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
182 base::FilePath virtual_path
;
185 void ProcessRecurseQueue() {
186 while (display_stack_
.empty() && !recurse_queue_
.empty()) {
187 FileRecord entry
= recurse_queue_
.front();
188 recurse_queue_
.pop();
189 if (!db_
->ListChildren(entry
.file_id
, &display_stack_
)) {
190 display_stack_
.clear();
193 current_parent_virtual_path_
= entry
.virtual_path
;
197 SandboxDirectoryDatabase
* db_
;
198 FileSystemOperationContext
* context_
;
199 ObfuscatedFileUtil
* obfuscated_file_util_
;
200 FileSystemURL root_url_
;
203 std::queue
<FileRecord
> recurse_queue_
;
204 std::vector
<FileId
> display_stack_
;
205 base::FilePath current_parent_virtual_path_
;
207 FileId current_file_id_
;
208 base::PlatformFileInfo current_platform_file_info_
;
211 class ObfuscatedOriginEnumerator
212 : public ObfuscatedFileUtil::AbstractOriginEnumerator
{
214 typedef SandboxOriginDatabase::OriginRecord OriginRecord
;
215 ObfuscatedOriginEnumerator(
216 SandboxOriginDatabaseInterface
* origin_database
,
217 const base::FilePath
& base_file_path
)
218 : base_file_path_(base_file_path
) {
220 origin_database
->ListAllOrigins(&origins_
);
223 virtual ~ObfuscatedOriginEnumerator() {}
225 // Returns the next origin. Returns empty if there are no more origins.
226 virtual GURL
Next() OVERRIDE
{
228 if (!origins_
.empty()) {
229 record
= origins_
.back();
233 return webkit_database::GetOriginFromIdentifier(record
.origin
);
236 // Returns the current origin's information.
237 virtual bool HasTypeDirectory(const std::string
& type_string
) const OVERRIDE
{
238 if (current_
.path
.empty())
240 if (type_string
.empty()) {
244 base::FilePath path
=
245 base_file_path_
.Append(current_
.path
).AppendASCII(type_string
);
246 return base::DirectoryExists(path
);
250 std::vector
<OriginRecord
> origins_
;
251 OriginRecord current_
;
252 base::FilePath base_file_path_
;
255 ObfuscatedFileUtil::ObfuscatedFileUtil(
256 quota::SpecialStoragePolicy
* special_storage_policy
,
257 const base::FilePath
& file_system_directory
,
258 base::SequencedTaskRunner
* file_task_runner
,
259 const GetTypeStringForURLCallback
& get_type_string_for_url
,
260 const std::set
<std::string
>& known_type_strings
,
261 SandboxFileSystemBackendDelegate
* sandbox_delegate
)
262 : special_storage_policy_(special_storage_policy
),
263 file_system_directory_(file_system_directory
),
264 db_flush_delay_seconds_(10 * 60), // 10 mins.
265 file_task_runner_(file_task_runner
),
266 get_type_string_for_url_(get_type_string_for_url
),
267 known_type_strings_(known_type_strings
),
268 sandbox_delegate_(sandbox_delegate
) {
271 ObfuscatedFileUtil::~ObfuscatedFileUtil() {
275 PlatformFileError
ObfuscatedFileUtil::CreateOrOpen(
276 FileSystemOperationContext
* context
,
277 const FileSystemURL
& url
, int file_flags
,
278 PlatformFile
* file_handle
, bool* created
) {
279 PlatformFileError error
= CreateOrOpenInternal(context
, url
, file_flags
,
280 file_handle
, created
);
281 if (*file_handle
!= base::kInvalidPlatformFileValue
&&
282 file_flags
& base::PLATFORM_FILE_WRITE
&&
283 context
->quota_limit_type() == quota::kQuotaLimitTypeUnlimited
&&
285 DCHECK_EQ(base::PLATFORM_FILE_OK
, error
);
286 sandbox_delegate_
->StickyInvalidateUsageCache(url
.origin(), url
.type());
291 PlatformFileError
ObfuscatedFileUtil::Close(
292 FileSystemOperationContext
* context
,
293 base::PlatformFile file
) {
294 return NativeFileUtil::Close(file
);
297 PlatformFileError
ObfuscatedFileUtil::EnsureFileExists(
298 FileSystemOperationContext
* context
,
299 const FileSystemURL
& url
,
301 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
303 return base::PLATFORM_FILE_ERROR_FAILED
;
306 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
308 if (!db
->GetFileInfo(file_id
, &file_info
)) {
310 return base::PLATFORM_FILE_ERROR_FAILED
;
312 if (file_info
.is_directory())
313 return base::PLATFORM_FILE_ERROR_NOT_A_FILE
;
316 return base::PLATFORM_FILE_OK
;
319 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
320 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
323 InitFileInfo(&file_info
, parent_id
,
324 VirtualPath::BaseName(url
.path()).value());
326 int64 growth
= UsageForPath(file_info
.name
.size());
327 if (!AllocateQuota(context
, growth
))
328 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
329 PlatformFileError error
= CreateFile(
330 context
, base::FilePath(), url
, &file_info
, 0, NULL
);
331 if (created
&& base::PLATFORM_FILE_OK
== error
) {
333 UpdateUsage(context
, url
, growth
);
334 context
->change_observers()->Notify(
335 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
340 PlatformFileError
ObfuscatedFileUtil::CreateDirectory(
341 FileSystemOperationContext
* context
,
342 const FileSystemURL
& url
,
345 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
347 return base::PLATFORM_FILE_ERROR_FAILED
;
350 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
353 return base::PLATFORM_FILE_ERROR_EXISTS
;
354 if (!db
->GetFileInfo(file_id
, &file_info
)) {
356 return base::PLATFORM_FILE_ERROR_FAILED
;
358 if (!file_info
.is_directory())
359 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
360 return base::PLATFORM_FILE_OK
;
363 std::vector
<base::FilePath::StringType
> components
;
364 VirtualPath::GetComponents(url
.path(), &components
);
365 FileId parent_id
= 0;
367 for (index
= 0; index
< components
.size(); ++index
) {
368 base::FilePath::StringType name
= components
[index
];
369 if (name
== FILE_PATH_LITERAL("/"))
371 if (!db
->GetChildWithName(parent_id
, name
, &parent_id
))
374 if (!recursive
&& components
.size() - index
> 1)
375 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
377 for (; index
< components
.size(); ++index
) {
379 file_info
.name
= components
[index
];
380 if (file_info
.name
== FILE_PATH_LITERAL("/"))
382 file_info
.modification_time
= base::Time::Now();
383 file_info
.parent_id
= parent_id
;
384 int64 growth
= UsageForPath(file_info
.name
.size());
385 if (!AllocateQuota(context
, growth
))
386 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
387 base::PlatformFileError error
= db
->AddFileInfo(file_info
, &parent_id
);
388 if (error
!= base::PLATFORM_FILE_OK
)
390 UpdateUsage(context
, url
, growth
);
391 context
->change_observers()->Notify(
392 &FileChangeObserver::OnCreateDirectory
, MakeTuple(url
));
395 TouchDirectory(db
, file_info
.parent_id
);
398 return base::PLATFORM_FILE_OK
;
401 PlatformFileError
ObfuscatedFileUtil::GetFileInfo(
402 FileSystemOperationContext
* context
,
403 const FileSystemURL
& url
,
404 base::PlatformFileInfo
* file_info
,
405 base::FilePath
* platform_file_path
) {
406 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
408 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
410 if (!db
->GetFileWithPath(url
.path(), &file_id
))
411 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
413 return GetFileInfoInternal(db
, context
, url
,
414 file_id
, &local_info
,
415 file_info
, platform_file_path
);
418 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
419 ObfuscatedFileUtil::CreateFileEnumerator(
420 FileSystemOperationContext
* context
,
421 const FileSystemURL
& root_url
) {
422 return CreateFileEnumerator(context
, root_url
, false /* recursive */);
425 PlatformFileError
ObfuscatedFileUtil::GetLocalFilePath(
426 FileSystemOperationContext
* context
,
427 const FileSystemURL
& url
,
428 base::FilePath
* local_path
) {
429 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
431 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
433 if (!db
->GetFileWithPath(url
.path(), &file_id
))
434 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
436 if (!db
->GetFileInfo(file_id
, &file_info
) || file_info
.is_directory()) {
438 // Directories have no local file path.
439 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
441 *local_path
= DataPathToLocalPath(url
, file_info
.data_path
);
443 if (local_path
->empty())
444 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
445 return base::PLATFORM_FILE_OK
;
448 PlatformFileError
ObfuscatedFileUtil::Touch(
449 FileSystemOperationContext
* context
,
450 const FileSystemURL
& url
,
451 const base::Time
& last_access_time
,
452 const base::Time
& last_modified_time
) {
453 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
455 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
457 if (!db
->GetFileWithPath(url
.path(), &file_id
))
458 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
461 if (!db
->GetFileInfo(file_id
, &file_info
)) {
463 return base::PLATFORM_FILE_ERROR_FAILED
;
465 if (file_info
.is_directory()) {
466 if (!db
->UpdateModificationTime(file_id
, last_modified_time
))
467 return base::PLATFORM_FILE_ERROR_FAILED
;
468 return base::PLATFORM_FILE_OK
;
470 return NativeFileUtil::Touch(
471 DataPathToLocalPath(url
, file_info
.data_path
),
472 last_access_time
, last_modified_time
);
475 PlatformFileError
ObfuscatedFileUtil::Truncate(
476 FileSystemOperationContext
* context
,
477 const FileSystemURL
& url
,
479 base::PlatformFileInfo file_info
;
480 base::FilePath local_path
;
481 base::PlatformFileError error
=
482 GetFileInfo(context
, url
, &file_info
, &local_path
);
483 if (error
!= base::PLATFORM_FILE_OK
)
486 int64 growth
= length
- file_info
.size
;
487 if (!AllocateQuota(context
, growth
))
488 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
489 error
= NativeFileUtil::Truncate(local_path
, length
);
490 if (error
== base::PLATFORM_FILE_OK
) {
491 UpdateUsage(context
, url
, growth
);
492 context
->change_observers()->Notify(
493 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
498 PlatformFileError
ObfuscatedFileUtil::CopyOrMoveFile(
499 FileSystemOperationContext
* context
,
500 const FileSystemURL
& src_url
,
501 const FileSystemURL
& dest_url
,
502 CopyOrMoveOption option
,
504 // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
505 DCHECK(src_url
.origin() == dest_url
.origin());
506 DCHECK(src_url
.type() == dest_url
.type());
508 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(src_url
, true);
510 return base::PLATFORM_FILE_ERROR_FAILED
;
513 if (!db
->GetFileWithPath(src_url
.path(), &src_file_id
))
514 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
517 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
520 FileInfo src_file_info
;
521 base::PlatformFileInfo src_platform_file_info
;
522 base::FilePath src_local_path
;
523 base::PlatformFileError error
= GetFileInfoInternal(
524 db
, context
, src_url
, src_file_id
,
525 &src_file_info
, &src_platform_file_info
, &src_local_path
);
526 if (error
!= base::PLATFORM_FILE_OK
)
528 if (src_file_info
.is_directory())
529 return base::PLATFORM_FILE_ERROR_NOT_A_FILE
;
531 FileInfo dest_file_info
;
532 base::PlatformFileInfo dest_platform_file_info
; // overwrite case only
533 base::FilePath dest_local_path
; // overwrite case only
535 base::PlatformFileError error
= GetFileInfoInternal(
536 db
, context
, dest_url
, dest_file_id
,
537 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
538 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
539 overwrite
= false; // fallback to non-overwrite case
540 else if (error
!= base::PLATFORM_FILE_OK
)
542 else if (dest_file_info
.is_directory())
543 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION
;
546 FileId dest_parent_id
;
547 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
549 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
552 dest_file_info
= src_file_info
;
553 dest_file_info
.parent_id
= dest_parent_id
;
554 dest_file_info
.name
=
555 VirtualPath::BaseName(dest_url
.path()).value();
560 growth
+= src_platform_file_info
.size
;
562 growth
-= UsageForPath(src_file_info
.name
.size());
564 growth
-= dest_platform_file_info
.size
;
566 growth
+= UsageForPath(dest_file_info
.name
.size());
567 if (!AllocateQuota(context
, growth
))
568 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
571 * Copy-with-overwrite
572 * Just overwrite data file
573 * Copy-without-overwrite
575 * Create new metadata pointing to new backing file.
576 * Move-with-overwrite
578 * Remove source entry.
579 * Point target entry to source entry's backing file.
580 * Delete target entry's old backing file
581 * Move-without-overwrite
582 * Just update metadata
584 error
= base::PLATFORM_FILE_ERROR_FAILED
;
587 error
= NativeFileUtil::CopyOrMoveFile(
592 } else { // non-overwrite
593 error
= CreateFile(context
, src_local_path
,
594 dest_url
, &dest_file_info
, 0, NULL
);
598 if (db
->OverwritingMoveFile(src_file_id
, dest_file_id
)) {
599 if (base::PLATFORM_FILE_OK
!=
600 NativeFileUtil::DeleteFile(dest_local_path
))
601 LOG(WARNING
) << "Leaked a backing file.";
602 error
= base::PLATFORM_FILE_OK
;
604 error
= base::PLATFORM_FILE_ERROR_FAILED
;
606 } else { // non-overwrite
607 if (db
->UpdateFileInfo(src_file_id
, dest_file_info
))
608 error
= base::PLATFORM_FILE_OK
;
610 error
= base::PLATFORM_FILE_ERROR_FAILED
;
614 if (error
!= base::PLATFORM_FILE_OK
)
618 context
->change_observers()->Notify(
619 &FileChangeObserver::OnModifyFile
,
620 MakeTuple(dest_url
));
622 context
->change_observers()->Notify(
623 &FileChangeObserver::OnCreateFileFrom
,
624 MakeTuple(dest_url
, src_url
));
628 context
->change_observers()->Notify(
629 &FileChangeObserver::OnRemoveFile
, MakeTuple(src_url
));
630 TouchDirectory(db
, src_file_info
.parent_id
);
633 TouchDirectory(db
, dest_file_info
.parent_id
);
635 UpdateUsage(context
, dest_url
, growth
);
639 PlatformFileError
ObfuscatedFileUtil::CopyInForeignFile(
640 FileSystemOperationContext
* context
,
641 const base::FilePath
& src_file_path
,
642 const FileSystemURL
& dest_url
) {
643 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
645 return base::PLATFORM_FILE_ERROR_FAILED
;
647 base::PlatformFileInfo src_platform_file_info
;
648 if (!file_util::GetFileInfo(src_file_path
, &src_platform_file_info
))
649 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
652 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
655 FileInfo dest_file_info
;
656 base::PlatformFileInfo dest_platform_file_info
; // overwrite case only
658 base::FilePath dest_local_path
;
659 base::PlatformFileError error
= GetFileInfoInternal(
660 db
, context
, dest_url
, dest_file_id
,
661 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
662 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
663 overwrite
= false; // fallback to non-overwrite case
664 else if (error
!= base::PLATFORM_FILE_OK
)
666 else if (dest_file_info
.is_directory())
667 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION
;
670 FileId dest_parent_id
;
671 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
673 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
675 if (!dest_file_info
.is_directory())
676 return base::PLATFORM_FILE_ERROR_FAILED
;
677 InitFileInfo(&dest_file_info
, dest_parent_id
,
678 VirtualPath::BaseName(dest_url
.path()).value());
681 int64 growth
= src_platform_file_info
.size
;
683 growth
-= dest_platform_file_info
.size
;
685 growth
+= UsageForPath(dest_file_info
.name
.size());
686 if (!AllocateQuota(context
, growth
))
687 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
689 base::PlatformFileError error
;
691 base::FilePath dest_local_path
=
692 DataPathToLocalPath(dest_url
, dest_file_info
.data_path
);
693 error
= NativeFileUtil::CopyOrMoveFile(
694 src_file_path
, dest_local_path
,
695 FileSystemOperation::OPTION_NONE
, true);
697 error
= CreateFile(context
, src_file_path
,
698 dest_url
, &dest_file_info
, 0, NULL
);
701 if (error
!= base::PLATFORM_FILE_OK
)
705 context
->change_observers()->Notify(
706 &FileChangeObserver::OnModifyFile
, MakeTuple(dest_url
));
708 context
->change_observers()->Notify(
709 &FileChangeObserver::OnCreateFile
, MakeTuple(dest_url
));
712 UpdateUsage(context
, dest_url
, growth
);
713 TouchDirectory(db
, dest_file_info
.parent_id
);
714 return base::PLATFORM_FILE_OK
;
717 PlatformFileError
ObfuscatedFileUtil::DeleteFile(
718 FileSystemOperationContext
* context
,
719 const FileSystemURL
& url
) {
720 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
722 return base::PLATFORM_FILE_ERROR_FAILED
;
724 if (!db
->GetFileWithPath(url
.path(), &file_id
))
725 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
728 base::PlatformFileInfo platform_file_info
;
729 base::FilePath local_path
;
730 base::PlatformFileError error
= GetFileInfoInternal(
731 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
732 if (error
!= base::PLATFORM_FILE_ERROR_NOT_FOUND
&&
733 error
!= base::PLATFORM_FILE_OK
)
736 if (file_info
.is_directory())
737 return base::PLATFORM_FILE_ERROR_NOT_A_FILE
;
739 int64 growth
= -UsageForPath(file_info
.name
.size()) - platform_file_info
.size
;
740 AllocateQuota(context
, growth
);
741 if (!db
->RemoveFileInfo(file_id
)) {
743 return base::PLATFORM_FILE_ERROR_FAILED
;
745 UpdateUsage(context
, url
, growth
);
746 TouchDirectory(db
, file_info
.parent_id
);
748 context
->change_observers()->Notify(
749 &FileChangeObserver::OnRemoveFile
, MakeTuple(url
));
751 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
752 return base::PLATFORM_FILE_OK
;
754 error
= NativeFileUtil::DeleteFile(local_path
);
755 if (base::PLATFORM_FILE_OK
!= error
)
756 LOG(WARNING
) << "Leaked a backing file.";
757 return base::PLATFORM_FILE_OK
;
760 PlatformFileError
ObfuscatedFileUtil::DeleteDirectory(
761 FileSystemOperationContext
* context
,
762 const FileSystemURL
& url
) {
763 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
765 return base::PLATFORM_FILE_ERROR_FAILED
;
768 if (!db
->GetFileWithPath(url
.path(), &file_id
))
769 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
771 if (!db
->GetFileInfo(file_id
, &file_info
)) {
773 return base::PLATFORM_FILE_ERROR_FAILED
;
775 if (!file_info
.is_directory())
776 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
777 if (!db
->RemoveFileInfo(file_id
))
778 return base::PLATFORM_FILE_ERROR_NOT_EMPTY
;
779 int64 growth
= -UsageForPath(file_info
.name
.size());
780 AllocateQuota(context
, growth
);
781 UpdateUsage(context
, url
, growth
);
782 TouchDirectory(db
, file_info
.parent_id
);
783 context
->change_observers()->Notify(
784 &FileChangeObserver::OnRemoveDirectory
, MakeTuple(url
));
785 return base::PLATFORM_FILE_OK
;
788 webkit_blob::ScopedFile
ObfuscatedFileUtil::CreateSnapshotFile(
789 FileSystemOperationContext
* context
,
790 const FileSystemURL
& url
,
791 base::PlatformFileError
* error
,
792 base::PlatformFileInfo
* file_info
,
793 base::FilePath
* platform_path
) {
794 // We're just returning the local file information.
795 *error
= GetFileInfo(context
, url
, file_info
, platform_path
);
796 if (*error
== base::PLATFORM_FILE_OK
&& file_info
->is_directory
) {
797 *file_info
= base::PlatformFileInfo();
798 *error
= base::PLATFORM_FILE_ERROR_NOT_A_FILE
;
800 return webkit_blob::ScopedFile();
803 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
804 ObfuscatedFileUtil::CreateFileEnumerator(
805 FileSystemOperationContext
* context
,
806 const FileSystemURL
& root_url
,
808 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(root_url
, false);
810 return scoped_ptr
<AbstractFileEnumerator
>(new EmptyFileEnumerator());
812 return scoped_ptr
<AbstractFileEnumerator
>(
813 new ObfuscatedFileEnumerator(db
, context
, this, root_url
, recursive
));
816 bool ObfuscatedFileUtil::IsDirectoryEmpty(
817 FileSystemOperationContext
* context
,
818 const FileSystemURL
& url
) {
819 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
821 return true; // Not a great answer, but it's what others do.
823 if (!db
->GetFileWithPath(url
.path(), &file_id
))
824 return true; // Ditto.
826 if (!db
->GetFileInfo(file_id
, &file_info
)) {
828 // It's the root directory and the database hasn't been initialized yet.
831 if (!file_info
.is_directory())
833 std::vector
<FileId
> children
;
834 // TODO(ericu): This could easily be made faster with help from the database.
835 if (!db
->ListChildren(file_id
, &children
))
837 return children
.empty();
840 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOriginAndType(
842 const std::string
& type_string
,
844 base::PlatformFileError
* error_code
) {
845 base::FilePath origin_dir
= GetDirectoryForOrigin(origin
, create
, error_code
);
846 if (origin_dir
.empty())
847 return base::FilePath();
848 if (type_string
.empty())
850 base::FilePath path
= origin_dir
.AppendASCII(type_string
);
851 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
852 if (!base::DirectoryExists(path
) &&
853 (!create
|| !file_util::CreateDirectory(path
))) {
855 base::PLATFORM_FILE_ERROR_FAILED
:
856 base::PLATFORM_FILE_ERROR_NOT_FOUND
;
864 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
866 const std::string
& type_string
) {
867 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
868 base::FilePath origin_type_path
= GetDirectoryForOriginAndType(
869 origin
, type_string
, false, &error
);
870 if (origin_type_path
.empty())
872 if (error
!= base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
873 // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase.
874 // We ignore its error now since 1) it doesn't matter the final result, and
875 // 2) it always returns false in Windows because of LevelDB's
877 // Information about failure would be useful for debugging.
878 if (!type_string
.empty())
879 DestroyDirectoryDatabase(origin
, type_string
);
880 if (!base::DeleteFile(origin_type_path
, true /* recursive */))
884 base::FilePath origin_path
= VirtualPath::DirName(origin_type_path
);
885 DCHECK_EQ(origin_path
.value(),
886 GetDirectoryForOrigin(origin
, false, NULL
).value());
888 if (!type_string
.empty()) {
889 // At this point we are sure we had successfully deleted the origin/type
890 // directory (i.e. we're ready to just return true).
891 // See if we have other directories in this origin directory.
892 for (std::set
<std::string
>::iterator iter
= known_type_strings_
.begin();
893 iter
!= known_type_strings_
.end();
895 if (*iter
== type_string
)
897 if (base::DirectoryExists(origin_path
.AppendASCII(*iter
))) {
898 // Other type's directory exists; just return true here.
904 // No other directories seem exist. Try deleting the entire origin directory.
905 InitOriginDatabase(origin
, false);
906 if (origin_database_
) {
907 origin_database_
->RemovePathForOrigin(
908 webkit_database::GetIdentifierFromOrigin(origin
));
910 if (!base::DeleteFile(origin_path
, true /* recursive */))
916 ObfuscatedFileUtil::AbstractOriginEnumerator
*
917 ObfuscatedFileUtil::CreateOriginEnumerator() {
918 std::vector
<SandboxOriginDatabase::OriginRecord
> origins
;
920 InitOriginDatabase(GURL(), false);
921 return new ObfuscatedOriginEnumerator(
922 origin_database_
.get(), file_system_directory_
);
925 bool ObfuscatedFileUtil::DestroyDirectoryDatabase(
927 const std::string
& type_string
) {
928 std::string key
= GetDirectoryDatabaseKey(origin
, type_string
);
931 DirectoryMap::iterator iter
= directories_
.find(key
);
932 if (iter
!= directories_
.end()) {
933 SandboxDirectoryDatabase
* database
= iter
->second
;
934 directories_
.erase(iter
);
938 PlatformFileError error
= base::PLATFORM_FILE_OK
;
939 base::FilePath path
= GetDirectoryForOriginAndType(
940 origin
, type_string
, false, &error
);
941 if (path
.empty() || error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
943 return SandboxDirectoryDatabase::DestroyDatabase(path
);
947 int64
ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath
& path
) {
948 return UsageForPath(VirtualPath::BaseName(path
).value().size());
951 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
952 const std::vector
<std::string
>& type_strings_to_prepopulate
) {
953 SandboxPrioritizedOriginDatabase
database(file_system_directory_
);
954 std::string origin_string
= database
.GetPrimaryOrigin();
955 if (origin_string
.empty() || !database
.HasOriginPath(origin_string
))
957 const GURL origin
= webkit_database::GetOriginFromIdentifier(origin_string
);
959 // Prepopulate the directory database(s) if and only if this instance
960 // has primary origin and the directory database is already there.
961 for (size_t i
= 0; i
< type_strings_to_prepopulate
.size(); ++i
) {
962 const std::string type_string
= type_strings_to_prepopulate
[i
];
963 // Only handles known types.
964 if (!ContainsKey(known_type_strings_
, type_string
))
966 PlatformFileError error
= base::PLATFORM_FILE_ERROR_FAILED
;
967 base::FilePath path
= GetDirectoryForOriginAndType(
968 origin
, type_string
, false, &error
);
969 if (error
!= base::PLATFORM_FILE_OK
)
971 scoped_ptr
<SandboxDirectoryDatabase
> db(new SandboxDirectoryDatabase(path
));
972 if (db
->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION
)) {
973 directories_
[GetDirectoryDatabaseKey(origin
, type_string
)] = db
.release();
975 // Don't populate more than one database, as it may rather hurt
982 base::FilePath
ObfuscatedFileUtil::GetDirectoryForURL(
983 const FileSystemURL
& url
,
985 base::PlatformFileError
* error_code
) {
986 return GetDirectoryForOriginAndType(
987 url
.origin(), CallGetTypeStringForURL(url
), create
, error_code
);
990 std::string
ObfuscatedFileUtil::CallGetTypeStringForURL(
991 const FileSystemURL
& url
) {
992 DCHECK(!get_type_string_for_url_
.is_null());
993 return get_type_string_for_url_
.Run(url
);
996 PlatformFileError
ObfuscatedFileUtil::GetFileInfoInternal(
997 SandboxDirectoryDatabase
* db
,
998 FileSystemOperationContext
* context
,
999 const FileSystemURL
& url
,
1001 FileInfo
* local_info
,
1002 base::PlatformFileInfo
* file_info
,
1003 base::FilePath
* platform_file_path
) {
1007 DCHECK(platform_file_path
);
1009 if (!db
->GetFileInfo(file_id
, local_info
)) {
1011 return base::PLATFORM_FILE_ERROR_FAILED
;
1014 if (local_info
->is_directory()) {
1015 file_info
->size
= 0;
1016 file_info
->is_directory
= true;
1017 file_info
->is_symbolic_link
= false;
1018 file_info
->last_modified
= local_info
->modification_time
;
1019 *platform_file_path
= base::FilePath();
1020 // We don't fill in ctime or atime.
1021 return base::PLATFORM_FILE_OK
;
1023 if (local_info
->data_path
.empty())
1024 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION
;
1025 base::FilePath local_path
= DataPathToLocalPath(url
, local_info
->data_path
);
1026 base::PlatformFileError error
= NativeFileUtil::GetFileInfo(
1027 local_path
, file_info
);
1028 // We should not follow symbolic links in sandboxed file system.
1029 if (file_util::IsLink(local_path
)) {
1030 LOG(WARNING
) << "Found a symbolic file.";
1031 error
= base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1033 if (error
== base::PLATFORM_FILE_OK
) {
1034 *platform_file_path
= local_path
;
1035 } else if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
1036 LOG(WARNING
) << "Lost a backing file.";
1037 InvalidateUsageCache(context
, url
.origin(), url
.type());
1038 if (!db
->RemoveFileInfo(file_id
))
1039 return base::PLATFORM_FILE_ERROR_FAILED
;
1044 PlatformFileError
ObfuscatedFileUtil::CreateFile(
1045 FileSystemOperationContext
* context
,
1046 const base::FilePath
& src_file_path
,
1047 const FileSystemURL
& dest_url
,
1048 FileInfo
* dest_file_info
, int file_flags
, PlatformFile
* handle
) {
1050 *handle
= base::kInvalidPlatformFileValue
;
1051 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1053 PlatformFileError error
= base::PLATFORM_FILE_OK
;
1054 base::FilePath root
= GetDirectoryForURL(dest_url
, false, &error
);
1055 if (error
!= base::PLATFORM_FILE_OK
)
1058 base::FilePath dest_local_path
;
1059 error
= GenerateNewLocalPath(db
, context
, dest_url
, &dest_local_path
);
1060 if (error
!= base::PLATFORM_FILE_OK
)
1063 bool created
= false;
1064 if (!src_file_path
.empty()) {
1065 DCHECK(!file_flags
);
1067 error
= NativeFileUtil::CopyOrMoveFile(
1068 src_file_path
, dest_local_path
,
1069 FileSystemOperation::OPTION_NONE
, true /* copy */);
1072 if (base::PathExists(dest_local_path
)) {
1073 if (!base::DeleteFile(dest_local_path
, true /* recursive */)) {
1075 return base::PLATFORM_FILE_ERROR_FAILED
;
1077 LOG(WARNING
) << "A stray file detected";
1078 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1082 error
= NativeFileUtil::CreateOrOpen(
1083 dest_local_path
, file_flags
, handle
, &created
);
1084 // If this succeeds, we must close handle on any subsequent error.
1086 DCHECK(!file_flags
); // file_flags is only used by CreateOrOpen.
1087 error
= NativeFileUtil::EnsureFileExists(dest_local_path
, &created
);
1090 if (error
!= base::PLATFORM_FILE_OK
)
1096 DCHECK_NE(base::kInvalidPlatformFileValue
, *handle
);
1097 base::ClosePlatformFile(*handle
);
1098 base::DeleteFile(dest_local_path
, false /* recursive */);
1099 *handle
= base::kInvalidPlatformFileValue
;
1101 return base::PLATFORM_FILE_ERROR_FAILED
;
1104 // This removes the root, including the trailing slash, leaving a relative
1106 dest_file_info
->data_path
= base::FilePath(
1107 dest_local_path
.value().substr(root
.value().length() + 1));
1110 error
= db
->AddFileInfo(*dest_file_info
, &file_id
);
1111 if (error
!= base::PLATFORM_FILE_OK
) {
1113 DCHECK_NE(base::kInvalidPlatformFileValue
, *handle
);
1114 base::ClosePlatformFile(*handle
);
1115 *handle
= base::kInvalidPlatformFileValue
;
1117 base::DeleteFile(dest_local_path
, false /* recursive */);
1120 TouchDirectory(db
, dest_file_info
->parent_id
);
1122 return base::PLATFORM_FILE_OK
;
1125 base::FilePath
ObfuscatedFileUtil::DataPathToLocalPath(
1126 const FileSystemURL
& url
, const base::FilePath
& data_path
) {
1127 PlatformFileError error
= base::PLATFORM_FILE_OK
;
1128 base::FilePath root
= GetDirectoryForURL(url
, false, &error
);
1129 if (error
!= base::PLATFORM_FILE_OK
)
1130 return base::FilePath();
1131 return root
.Append(data_path
);
1134 std::string
ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1135 const GURL
& origin
, const std::string
& type_string
) {
1136 if (type_string
.empty()) {
1137 LOG(WARNING
) << "Unknown filesystem type requested:" << type_string
;
1138 return std::string();
1140 // For isolated origin we just use a type string as a key.
1141 return webkit_database::GetIdentifierFromOrigin(origin
) +
1145 // TODO(ericu): How to do the whole validation-without-creation thing?
1146 // We may not have quota even to create the database.
1147 // Ah, in that case don't even get here?
1148 // Still doesn't answer the quota issue, though.
1149 SandboxDirectoryDatabase
* ObfuscatedFileUtil::GetDirectoryDatabase(
1150 const FileSystemURL
& url
, bool create
) {
1151 std::string key
= GetDirectoryDatabaseKey(
1152 url
.origin(), CallGetTypeStringForURL(url
));
1156 DirectoryMap::iterator iter
= directories_
.find(key
);
1157 if (iter
!= directories_
.end()) {
1159 return iter
->second
;
1162 PlatformFileError error
= base::PLATFORM_FILE_OK
;
1163 base::FilePath path
= GetDirectoryForURL(url
, create
, &error
);
1164 if (error
!= base::PLATFORM_FILE_OK
) {
1165 LOG(WARNING
) << "Failed to get origin+type directory: " << path
.value();
1169 SandboxDirectoryDatabase
* database
= new SandboxDirectoryDatabase(path
);
1170 directories_
[key
] = database
;
1174 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOrigin(
1175 const GURL
& origin
, bool create
, base::PlatformFileError
* error_code
) {
1176 if (!InitOriginDatabase(origin
, create
)) {
1178 *error_code
= create
?
1179 base::PLATFORM_FILE_ERROR_FAILED
:
1180 base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1182 return base::FilePath();
1184 base::FilePath directory_name
;
1185 std::string id
= webkit_database::GetIdentifierFromOrigin(origin
);
1187 bool exists_in_db
= origin_database_
->HasOriginPath(id
);
1188 if (!exists_in_db
&& !create
) {
1190 *error_code
= base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1191 return base::FilePath();
1193 if (!origin_database_
->GetPathForOrigin(id
, &directory_name
)) {
1195 *error_code
= base::PLATFORM_FILE_ERROR_FAILED
;
1196 return base::FilePath();
1199 base::FilePath path
= file_system_directory_
.Append(directory_name
);
1200 bool exists_in_fs
= base::DirectoryExists(path
);
1201 if (!exists_in_db
&& exists_in_fs
) {
1202 if (!base::DeleteFile(path
, true)) {
1204 *error_code
= base::PLATFORM_FILE_ERROR_FAILED
;
1205 return base::FilePath();
1207 exists_in_fs
= false;
1210 if (!exists_in_fs
) {
1211 if (!create
|| !file_util::CreateDirectory(path
)) {
1213 *error_code
= create
?
1214 base::PLATFORM_FILE_ERROR_FAILED
:
1215 base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1216 return base::FilePath();
1221 *error_code
= base::PLATFORM_FILE_OK
;
1226 void ObfuscatedFileUtil::InvalidateUsageCache(
1227 FileSystemOperationContext
* context
,
1229 FileSystemType type
) {
1230 if (sandbox_delegate_
)
1231 sandbox_delegate_
->InvalidateUsageCache(origin
, type
);
1234 void ObfuscatedFileUtil::MarkUsed() {
1236 timer_
.reset(new TimedTaskHelper(file_task_runner_
.get()));
1238 if (timer_
->IsRunning()) {
1241 timer_
->Start(FROM_HERE
,
1242 base::TimeDelta::FromSeconds(db_flush_delay_seconds_
),
1243 base::Bind(&ObfuscatedFileUtil::DropDatabases
,
1244 base::Unretained(this)));
1248 void ObfuscatedFileUtil::DropDatabases() {
1249 origin_database_
.reset();
1250 STLDeleteContainerPairSecondPointers(
1251 directories_
.begin(), directories_
.end());
1252 directories_
.clear();
1256 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL
& origin_hint
,
1258 if (origin_database_
)
1261 if (!create
&& !base::DirectoryExists(file_system_directory_
))
1263 if (!file_util::CreateDirectory(file_system_directory_
)) {
1264 LOG(WARNING
) << "Failed to create FileSystem directory: " <<
1265 file_system_directory_
.value();
1269 SandboxPrioritizedOriginDatabase
* prioritized_origin_database
=
1270 new SandboxPrioritizedOriginDatabase(file_system_directory_
);
1271 origin_database_
.reset(prioritized_origin_database
);
1273 if (origin_hint
.is_empty() || !HasIsolatedStorage(origin_hint
))
1276 const std::string isolated_origin_string
=
1277 webkit_database::GetIdentifierFromOrigin(origin_hint
);
1279 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1280 base::FilePath isolated_origin_dir
= file_system_directory_
.Append(
1281 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory
);
1282 if (base::DirectoryExists(isolated_origin_dir
) &&
1283 prioritized_origin_database
->GetSandboxOriginDatabase()) {
1284 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1285 isolated_origin_string
,
1286 file_system_directory_
,
1287 prioritized_origin_database
->GetSandboxOriginDatabase());
1290 prioritized_origin_database
->InitializePrimaryOrigin(
1291 isolated_origin_string
);
1296 PlatformFileError
ObfuscatedFileUtil::GenerateNewLocalPath(
1297 SandboxDirectoryDatabase
* db
,
1298 FileSystemOperationContext
* context
,
1299 const FileSystemURL
& url
,
1300 base::FilePath
* local_path
) {
1303 if (!db
|| !db
->GetNextInteger(&number
))
1304 return base::PLATFORM_FILE_ERROR_FAILED
;
1306 PlatformFileError error
= base::PLATFORM_FILE_OK
;
1307 base::FilePath new_local_path
= GetDirectoryForURL(url
, false, &error
);
1308 if (error
!= base::PLATFORM_FILE_OK
)
1309 return base::PLATFORM_FILE_ERROR_FAILED
;
1311 // We use the third- and fourth-to-last digits as the directory.
1312 int64 directory_number
= number
% 10000 / 100;
1313 new_local_path
= new_local_path
.AppendASCII(
1314 base::StringPrintf("%02" PRId64
, directory_number
));
1316 error
= NativeFileUtil::CreateDirectory(
1317 new_local_path
, false /* exclusive */, false /* recursive */);
1318 if (error
!= base::PLATFORM_FILE_OK
)
1322 new_local_path
.AppendASCII(base::StringPrintf("%08" PRId64
, number
));
1323 return base::PLATFORM_FILE_OK
;
1326 PlatformFileError
ObfuscatedFileUtil::CreateOrOpenInternal(
1327 FileSystemOperationContext
* context
,
1328 const FileSystemURL
& url
, int file_flags
,
1329 PlatformFile
* file_handle
, bool* created
) {
1330 DCHECK(!(file_flags
& (base::PLATFORM_FILE_DELETE_ON_CLOSE
|
1331 base::PLATFORM_FILE_HIDDEN
| base::PLATFORM_FILE_EXCLUSIVE_READ
|
1332 base::PLATFORM_FILE_EXCLUSIVE_WRITE
)));
1333 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
1335 return base::PLATFORM_FILE_ERROR_FAILED
;
1337 if (!db
->GetFileWithPath(url
.path(), &file_id
)) {
1338 // The file doesn't exist.
1339 if (!(file_flags
& (base::PLATFORM_FILE_CREATE
|
1340 base::PLATFORM_FILE_CREATE_ALWAYS
| base::PLATFORM_FILE_OPEN_ALWAYS
)))
1341 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1343 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()),
1345 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
1347 InitFileInfo(&file_info
, parent_id
,
1348 VirtualPath::BaseName(url
.path()).value());
1350 int64 growth
= UsageForPath(file_info
.name
.size());
1351 if (!AllocateQuota(context
, growth
))
1352 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
1353 PlatformFileError error
= CreateFile(
1354 context
, base::FilePath(),
1355 url
, &file_info
, file_flags
, file_handle
);
1356 if (created
&& base::PLATFORM_FILE_OK
== error
) {
1358 UpdateUsage(context
, url
, growth
);
1359 context
->change_observers()->Notify(
1360 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
1365 if (file_flags
& base::PLATFORM_FILE_CREATE
)
1366 return base::PLATFORM_FILE_ERROR_EXISTS
;
1368 base::PlatformFileInfo platform_file_info
;
1369 base::FilePath local_path
;
1371 base::PlatformFileError error
= GetFileInfoInternal(
1372 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
1373 if (error
!= base::PLATFORM_FILE_OK
)
1375 if (file_info
.is_directory())
1376 return base::PLATFORM_FILE_ERROR_NOT_A_FILE
;
1379 if (file_flags
& (base::PLATFORM_FILE_CREATE_ALWAYS
|
1380 base::PLATFORM_FILE_OPEN_TRUNCATED
)) {
1381 // The file exists and we're truncating.
1382 delta
= -platform_file_info
.size
;
1383 AllocateQuota(context
, delta
);
1386 error
= NativeFileUtil::CreateOrOpen(
1387 local_path
, file_flags
, file_handle
, created
);
1388 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
1389 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1390 // TODO(tzik): Delete database entry after ensuring the file lost.
1391 InvalidateUsageCache(context
, url
.origin(), url
.type());
1392 LOG(WARNING
) << "Lost a backing file.";
1393 error
= base::PLATFORM_FILE_ERROR_FAILED
;
1396 // If truncating we need to update the usage.
1397 if (error
== base::PLATFORM_FILE_OK
&& delta
) {
1398 UpdateUsage(context
, url
, delta
);
1399 context
->change_observers()->Notify(
1400 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
1405 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL
& origin
) {
1406 return special_storage_policy_
.get() &&
1407 special_storage_policy_
->HasIsolatedStorage(origin
);
1410 } // namespace fileapi