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 "storage/browser/fileapi/obfuscated_file_util.h"
11 #include "base/files/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/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/time/time.h"
23 #include "storage/browser/fileapi/file_observers.h"
24 #include "storage/browser/fileapi/file_system_context.h"
25 #include "storage/browser/fileapi/file_system_operation_context.h"
26 #include "storage/browser/fileapi/file_system_url.h"
27 #include "storage/browser/fileapi/native_file_util.h"
28 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
29 #include "storage/browser/fileapi/sandbox_isolated_origin_database.h"
30 #include "storage/browser/fileapi/sandbox_origin_database.h"
31 #include "storage/browser/fileapi/sandbox_prioritized_origin_database.h"
32 #include "storage/browser/fileapi/timed_task_helper.h"
33 #include "storage/browser/quota/quota_manager.h"
34 #include "storage/common/database/database_identifier.h"
35 #include "storage/common/fileapi/file_system_util.h"
38 // Example of various paths:
39 // void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
40 // base::FilePath virtual_path = url.path();
41 // base::FilePath local_path = GetLocalFilePath(url);
43 // NativeFileUtil::DoSomething(local_path);
44 // file_util::DoAnother(local_path);
51 typedef SandboxDirectoryDatabase::FileId FileId
;
52 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
55 SandboxDirectoryDatabase::FileInfo
* file_info
,
56 SandboxDirectoryDatabase::FileId parent_id
,
57 const base::FilePath::StringType
& file_name
) {
59 file_info
->parent_id
= parent_id
;
60 file_info
->name
= file_name
;
63 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of
64 // path storage under Linux. It's not clear if that will differ on Windows, on
65 // which base::FilePath uses wide chars [since they're converted to UTF-8 for
66 // storage anyway], but as long as the cost is high enough that one can't cheat
67 // on quota by storing data in paths, it doesn't need to be all that accurate.
68 const int64 kPathCreationQuotaCost
= 146; // Bytes per inode, basically.
69 const int64 kPathByteQuotaCost
= 2; // Bytes per byte of path length in UTF-8.
71 const char kDirectoryDatabaseKeySeparator
= ' ';
73 int64
UsageForPath(size_t length
) {
74 return kPathCreationQuotaCost
+
75 static_cast<int64
>(length
) * kPathByteQuotaCost
;
78 bool AllocateQuota(FileSystemOperationContext
* context
, int64 growth
) {
79 if (context
->allowed_bytes_growth() == storage::QuotaManager::kNoLimit
)
82 int64 new_quota
= context
->allowed_bytes_growth() - growth
;
83 if (growth
> 0 && new_quota
< 0)
85 context
->set_allowed_bytes_growth(new_quota
);
90 FileSystemOperationContext
* context
,
91 const FileSystemURL
& url
,
93 context
->update_observers()->Notify(
94 &FileUpdateObserver::OnUpdate
, MakeTuple(url
, growth
));
97 void TouchDirectory(SandboxDirectoryDatabase
* db
, FileId dir_id
) {
99 if (!db
->UpdateModificationTime(dir_id
, base::Time::Now()))
103 enum IsolatedOriginStatus
{
104 kIsolatedOriginMatch
,
105 kIsolatedOriginDontMatch
,
106 kIsolatedOriginStatusMax
,
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 ~ObfuscatedFileEnumerator() override
{}
138 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::File::Error 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::File::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 int64
Size() override
{ return current_platform_file_info_
.size
; }
166 base::Time
LastModifiedTime() override
{
167 return current_platform_file_info_
.last_modified
;
170 bool IsDirectory() override
{
171 return current_platform_file_info_
.is_directory
;
175 typedef SandboxDirectoryDatabase::FileId FileId
;
176 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
180 base::FilePath virtual_path
;
183 void ProcessRecurseQueue() {
184 while (display_stack_
.empty() && !recurse_queue_
.empty()) {
185 FileRecord entry
= recurse_queue_
.front();
186 recurse_queue_
.pop();
187 if (!db_
->ListChildren(entry
.file_id
, &display_stack_
)) {
188 display_stack_
.clear();
191 current_parent_virtual_path_
= entry
.virtual_path
;
195 SandboxDirectoryDatabase
* db_
;
196 FileSystemOperationContext
* context_
;
197 ObfuscatedFileUtil
* obfuscated_file_util_
;
198 FileSystemURL root_url_
;
201 std::queue
<FileRecord
> recurse_queue_
;
202 std::vector
<FileId
> display_stack_
;
203 base::FilePath current_parent_virtual_path_
;
205 FileId current_file_id_
;
206 base::File::Info current_platform_file_info_
;
209 class ObfuscatedOriginEnumerator
210 : public ObfuscatedFileUtil::AbstractOriginEnumerator
{
212 typedef SandboxOriginDatabase::OriginRecord OriginRecord
;
213 ObfuscatedOriginEnumerator(
214 SandboxOriginDatabaseInterface
* origin_database
,
215 const base::FilePath
& base_file_path
)
216 : base_file_path_(base_file_path
) {
218 origin_database
->ListAllOrigins(&origins_
);
221 ~ObfuscatedOriginEnumerator() override
{}
223 // Returns the next origin. Returns empty if there are no more origins.
224 GURL
Next() override
{
226 if (!origins_
.empty()) {
227 record
= origins_
.back();
231 return storage::GetOriginFromIdentifier(record
.origin
);
234 // Returns the current origin's information.
235 bool HasTypeDirectory(const std::string
& type_string
) const override
{
236 if (current_
.path
.empty())
238 if (type_string
.empty()) {
242 base::FilePath path
=
243 base_file_path_
.Append(current_
.path
).AppendASCII(type_string
);
244 return base::DirectoryExists(path
);
248 std::vector
<OriginRecord
> origins_
;
249 OriginRecord current_
;
250 base::FilePath base_file_path_
;
253 ObfuscatedFileUtil::ObfuscatedFileUtil(
254 storage::SpecialStoragePolicy
* special_storage_policy
,
255 const base::FilePath
& file_system_directory
,
256 leveldb::Env
* env_override
,
257 base::SequencedTaskRunner
* file_task_runner
,
258 const GetTypeStringForURLCallback
& get_type_string_for_url
,
259 const std::set
<std::string
>& known_type_strings
,
260 SandboxFileSystemBackendDelegate
* sandbox_delegate
)
261 : special_storage_policy_(special_storage_policy
),
262 file_system_directory_(file_system_directory
),
263 env_override_(env_override
),
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 base::File
ObfuscatedFileUtil::CreateOrOpen(
276 FileSystemOperationContext
* context
,
277 const FileSystemURL
& url
, int file_flags
) {
278 base::File file
= CreateOrOpenInternal(context
, url
, file_flags
);
279 if (file
.IsValid() && file_flags
& base::File::FLAG_WRITE
&&
280 context
->quota_limit_type() == storage::kQuotaLimitTypeUnlimited
&&
282 sandbox_delegate_
->StickyInvalidateUsageCache(url
.origin(), url
.type());
287 base::File::Error
ObfuscatedFileUtil::EnsureFileExists(
288 FileSystemOperationContext
* context
,
289 const FileSystemURL
& url
,
291 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
293 return base::File::FILE_ERROR_FAILED
;
296 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
298 if (!db
->GetFileInfo(file_id
, &file_info
)) {
300 return base::File::FILE_ERROR_FAILED
;
302 if (file_info
.is_directory())
303 return base::File::FILE_ERROR_NOT_A_FILE
;
306 return base::File::FILE_OK
;
309 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
310 return base::File::FILE_ERROR_NOT_FOUND
;
313 InitFileInfo(&file_info
, parent_id
,
314 VirtualPath::BaseName(url
.path()).value());
316 int64 growth
= UsageForPath(file_info
.name
.size());
317 if (!AllocateQuota(context
, growth
))
318 return base::File::FILE_ERROR_NO_SPACE
;
319 base::File::Error error
= CreateFile(context
, base::FilePath(), url
,
321 if (created
&& base::File::FILE_OK
== error
) {
323 UpdateUsage(context
, url
, growth
);
324 context
->change_observers()->Notify(
325 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
330 base::File::Error
ObfuscatedFileUtil::CreateDirectory(
331 FileSystemOperationContext
* context
,
332 const FileSystemURL
& url
,
335 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
337 return base::File::FILE_ERROR_FAILED
;
340 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
343 return base::File::FILE_ERROR_EXISTS
;
344 if (!db
->GetFileInfo(file_id
, &file_info
)) {
346 return base::File::FILE_ERROR_FAILED
;
348 if (!file_info
.is_directory())
349 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
350 return base::File::FILE_OK
;
353 std::vector
<base::FilePath::StringType
> components
;
354 VirtualPath::GetComponents(url
.path(), &components
);
355 FileId parent_id
= 0;
357 for (index
= 0; index
< components
.size(); ++index
) {
358 base::FilePath::StringType name
= components
[index
];
359 if (name
== FILE_PATH_LITERAL("/"))
361 if (!db
->GetChildWithName(parent_id
, name
, &parent_id
))
364 if (!db
->IsDirectory(parent_id
))
365 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
366 if (!recursive
&& components
.size() - index
> 1)
367 return base::File::FILE_ERROR_NOT_FOUND
;
369 for (; index
< components
.size(); ++index
) {
371 file_info
.name
= components
[index
];
372 if (file_info
.name
== FILE_PATH_LITERAL("/"))
374 file_info
.modification_time
= base::Time::Now();
375 file_info
.parent_id
= parent_id
;
376 int64 growth
= UsageForPath(file_info
.name
.size());
377 if (!AllocateQuota(context
, growth
))
378 return base::File::FILE_ERROR_NO_SPACE
;
379 base::File::Error error
= db
->AddFileInfo(file_info
, &parent_id
);
380 if (error
!= base::File::FILE_OK
)
382 UpdateUsage(context
, url
, growth
);
383 context
->change_observers()->Notify(
384 &FileChangeObserver::OnCreateDirectory
, MakeTuple(url
));
387 TouchDirectory(db
, file_info
.parent_id
);
390 return base::File::FILE_OK
;
393 base::File::Error
ObfuscatedFileUtil::GetFileInfo(
394 FileSystemOperationContext
* context
,
395 const FileSystemURL
& url
,
396 base::File::Info
* file_info
,
397 base::FilePath
* platform_file_path
) {
398 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
400 return base::File::FILE_ERROR_NOT_FOUND
;
402 if (!db
->GetFileWithPath(url
.path(), &file_id
))
403 return base::File::FILE_ERROR_NOT_FOUND
;
405 return GetFileInfoInternal(db
, context
, url
,
406 file_id
, &local_info
,
407 file_info
, platform_file_path
);
410 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
411 ObfuscatedFileUtil::CreateFileEnumerator(
412 FileSystemOperationContext
* context
,
413 const FileSystemURL
& root_url
) {
414 return CreateFileEnumerator(context
, root_url
, false /* recursive */);
417 base::File::Error
ObfuscatedFileUtil::GetLocalFilePath(
418 FileSystemOperationContext
* context
,
419 const FileSystemURL
& url
,
420 base::FilePath
* local_path
) {
421 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
423 return base::File::FILE_ERROR_NOT_FOUND
;
425 if (!db
->GetFileWithPath(url
.path(), &file_id
))
426 return base::File::FILE_ERROR_NOT_FOUND
;
428 if (!db
->GetFileInfo(file_id
, &file_info
) || file_info
.is_directory()) {
430 // Directories have no local file path.
431 return base::File::FILE_ERROR_NOT_FOUND
;
433 *local_path
= DataPathToLocalPath(url
, file_info
.data_path
);
435 if (local_path
->empty())
436 return base::File::FILE_ERROR_NOT_FOUND
;
437 return base::File::FILE_OK
;
440 base::File::Error
ObfuscatedFileUtil::Touch(
441 FileSystemOperationContext
* context
,
442 const FileSystemURL
& url
,
443 const base::Time
& last_access_time
,
444 const base::Time
& last_modified_time
) {
445 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
447 return base::File::FILE_ERROR_NOT_FOUND
;
449 if (!db
->GetFileWithPath(url
.path(), &file_id
))
450 return base::File::FILE_ERROR_NOT_FOUND
;
453 if (!db
->GetFileInfo(file_id
, &file_info
)) {
455 return base::File::FILE_ERROR_FAILED
;
457 if (file_info
.is_directory()) {
458 if (!db
->UpdateModificationTime(file_id
, last_modified_time
))
459 return base::File::FILE_ERROR_FAILED
;
460 return base::File::FILE_OK
;
462 return NativeFileUtil::Touch(
463 DataPathToLocalPath(url
, file_info
.data_path
),
464 last_access_time
, last_modified_time
);
467 base::File::Error
ObfuscatedFileUtil::Truncate(
468 FileSystemOperationContext
* context
,
469 const FileSystemURL
& url
,
471 base::File::Info file_info
;
472 base::FilePath local_path
;
473 base::File::Error error
=
474 GetFileInfo(context
, url
, &file_info
, &local_path
);
475 if (error
!= base::File::FILE_OK
)
478 int64 growth
= length
- file_info
.size
;
479 if (!AllocateQuota(context
, growth
))
480 return base::File::FILE_ERROR_NO_SPACE
;
481 error
= NativeFileUtil::Truncate(local_path
, length
);
482 if (error
== base::File::FILE_OK
) {
483 UpdateUsage(context
, url
, growth
);
484 context
->change_observers()->Notify(
485 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
490 base::File::Error
ObfuscatedFileUtil::CopyOrMoveFile(
491 FileSystemOperationContext
* context
,
492 const FileSystemURL
& src_url
,
493 const FileSystemURL
& dest_url
,
494 CopyOrMoveOption option
,
496 // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
497 DCHECK(src_url
.origin() == dest_url
.origin());
498 DCHECK(src_url
.type() == dest_url
.type());
500 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(src_url
, true);
502 return base::File::FILE_ERROR_FAILED
;
505 if (!db
->GetFileWithPath(src_url
.path(), &src_file_id
))
506 return base::File::FILE_ERROR_NOT_FOUND
;
509 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
512 FileInfo src_file_info
;
513 base::File::Info src_platform_file_info
;
514 base::FilePath src_local_path
;
515 base::File::Error error
= GetFileInfoInternal(
516 db
, context
, src_url
, src_file_id
,
517 &src_file_info
, &src_platform_file_info
, &src_local_path
);
518 if (error
!= base::File::FILE_OK
)
520 if (src_file_info
.is_directory())
521 return base::File::FILE_ERROR_NOT_A_FILE
;
523 FileInfo dest_file_info
;
524 base::File::Info dest_platform_file_info
; // overwrite case only
525 base::FilePath dest_local_path
; // overwrite case only
527 base::File::Error error
= GetFileInfoInternal(
528 db
, context
, dest_url
, dest_file_id
,
529 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
530 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
531 overwrite
= false; // fallback to non-overwrite case
532 else if (error
!= base::File::FILE_OK
)
534 else if (dest_file_info
.is_directory())
535 return base::File::FILE_ERROR_INVALID_OPERATION
;
538 FileId dest_parent_id
;
539 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
541 return base::File::FILE_ERROR_NOT_FOUND
;
544 dest_file_info
= src_file_info
;
545 dest_file_info
.parent_id
= dest_parent_id
;
546 dest_file_info
.name
=
547 VirtualPath::BaseName(dest_url
.path()).value();
552 growth
+= src_platform_file_info
.size
;
554 growth
-= UsageForPath(src_file_info
.name
.size());
556 growth
-= dest_platform_file_info
.size
;
558 growth
+= UsageForPath(dest_file_info
.name
.size());
559 if (!AllocateQuota(context
, growth
))
560 return base::File::FILE_ERROR_NO_SPACE
;
563 * Copy-with-overwrite
564 * Just overwrite data file
565 * Copy-without-overwrite
567 * Create new metadata pointing to new backing file.
568 * Move-with-overwrite
570 * Remove source entry.
571 * Point target entry to source entry's backing file.
572 * Delete target entry's old backing file
573 * Move-without-overwrite
574 * Just update metadata
576 error
= base::File::FILE_ERROR_FAILED
;
579 error
= NativeFileUtil::CopyOrMoveFile(
583 storage::NativeFileUtil::CopyOrMoveModeForDestination(
584 dest_url
, true /* copy */));
585 } else { // non-overwrite
586 error
= CreateFile(context
, src_local_path
, dest_url
, &dest_file_info
);
590 if (db
->OverwritingMoveFile(src_file_id
, dest_file_id
)) {
591 if (base::File::FILE_OK
!=
592 NativeFileUtil::DeleteFile(dest_local_path
))
593 LOG(WARNING
) << "Leaked a backing file.";
594 error
= base::File::FILE_OK
;
596 error
= base::File::FILE_ERROR_FAILED
;
598 } else { // non-overwrite
599 if (db
->UpdateFileInfo(src_file_id
, dest_file_info
))
600 error
= base::File::FILE_OK
;
602 error
= base::File::FILE_ERROR_FAILED
;
606 if (error
!= base::File::FILE_OK
)
610 context
->change_observers()->Notify(
611 &FileChangeObserver::OnModifyFile
,
612 MakeTuple(dest_url
));
614 context
->change_observers()->Notify(
615 &FileChangeObserver::OnCreateFileFrom
,
616 MakeTuple(dest_url
, src_url
));
620 context
->change_observers()->Notify(
621 &FileChangeObserver::OnRemoveFile
, MakeTuple(src_url
));
622 TouchDirectory(db
, src_file_info
.parent_id
);
625 TouchDirectory(db
, dest_file_info
.parent_id
);
627 UpdateUsage(context
, dest_url
, growth
);
631 base::File::Error
ObfuscatedFileUtil::CopyInForeignFile(
632 FileSystemOperationContext
* context
,
633 const base::FilePath
& src_file_path
,
634 const FileSystemURL
& dest_url
) {
635 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
637 return base::File::FILE_ERROR_FAILED
;
639 base::File::Info src_platform_file_info
;
640 if (!base::GetFileInfo(src_file_path
, &src_platform_file_info
))
641 return base::File::FILE_ERROR_NOT_FOUND
;
644 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
647 FileInfo dest_file_info
;
648 base::File::Info dest_platform_file_info
; // overwrite case only
650 base::FilePath dest_local_path
;
651 base::File::Error error
= GetFileInfoInternal(
652 db
, context
, dest_url
, dest_file_id
,
653 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
654 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
655 overwrite
= false; // fallback to non-overwrite case
656 else if (error
!= base::File::FILE_OK
)
658 else if (dest_file_info
.is_directory())
659 return base::File::FILE_ERROR_INVALID_OPERATION
;
662 FileId dest_parent_id
;
663 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
665 return base::File::FILE_ERROR_NOT_FOUND
;
667 if (!dest_file_info
.is_directory())
668 return base::File::FILE_ERROR_FAILED
;
669 InitFileInfo(&dest_file_info
, dest_parent_id
,
670 VirtualPath::BaseName(dest_url
.path()).value());
673 int64 growth
= src_platform_file_info
.size
;
675 growth
-= dest_platform_file_info
.size
;
677 growth
+= UsageForPath(dest_file_info
.name
.size());
678 if (!AllocateQuota(context
, growth
))
679 return base::File::FILE_ERROR_NO_SPACE
;
681 base::File::Error error
;
683 base::FilePath dest_local_path
=
684 DataPathToLocalPath(dest_url
, dest_file_info
.data_path
);
685 error
= NativeFileUtil::CopyOrMoveFile(
688 FileSystemOperation::OPTION_NONE
,
689 storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
692 error
= CreateFile(context
, src_file_path
, dest_url
, &dest_file_info
);
695 if (error
!= base::File::FILE_OK
)
699 context
->change_observers()->Notify(
700 &FileChangeObserver::OnModifyFile
, MakeTuple(dest_url
));
702 context
->change_observers()->Notify(
703 &FileChangeObserver::OnCreateFile
, MakeTuple(dest_url
));
706 UpdateUsage(context
, dest_url
, growth
);
707 TouchDirectory(db
, dest_file_info
.parent_id
);
708 return base::File::FILE_OK
;
711 base::File::Error
ObfuscatedFileUtil::DeleteFile(
712 FileSystemOperationContext
* context
,
713 const FileSystemURL
& url
) {
714 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
716 return base::File::FILE_ERROR_FAILED
;
718 if (!db
->GetFileWithPath(url
.path(), &file_id
))
719 return base::File::FILE_ERROR_NOT_FOUND
;
722 base::File::Info platform_file_info
;
723 base::FilePath local_path
;
724 base::File::Error error
= GetFileInfoInternal(
725 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
726 if (error
!= base::File::FILE_ERROR_NOT_FOUND
&&
727 error
!= base::File::FILE_OK
)
730 if (file_info
.is_directory())
731 return base::File::FILE_ERROR_NOT_A_FILE
;
733 int64 growth
= -UsageForPath(file_info
.name
.size()) - platform_file_info
.size
;
734 AllocateQuota(context
, growth
);
735 if (!db
->RemoveFileInfo(file_id
)) {
737 return base::File::FILE_ERROR_FAILED
;
739 UpdateUsage(context
, url
, growth
);
740 TouchDirectory(db
, file_info
.parent_id
);
742 context
->change_observers()->Notify(
743 &FileChangeObserver::OnRemoveFile
, MakeTuple(url
));
745 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
746 return base::File::FILE_OK
;
748 error
= NativeFileUtil::DeleteFile(local_path
);
749 if (base::File::FILE_OK
!= error
)
750 LOG(WARNING
) << "Leaked a backing file.";
751 return base::File::FILE_OK
;
754 base::File::Error
ObfuscatedFileUtil::DeleteDirectory(
755 FileSystemOperationContext
* context
,
756 const FileSystemURL
& url
) {
757 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
759 return base::File::FILE_ERROR_FAILED
;
762 if (!db
->GetFileWithPath(url
.path(), &file_id
))
763 return base::File::FILE_ERROR_NOT_FOUND
;
765 if (!db
->GetFileInfo(file_id
, &file_info
)) {
767 return base::File::FILE_ERROR_FAILED
;
769 if (!file_info
.is_directory())
770 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
771 if (!db
->RemoveFileInfo(file_id
))
772 return base::File::FILE_ERROR_NOT_EMPTY
;
773 int64 growth
= -UsageForPath(file_info
.name
.size());
774 AllocateQuota(context
, growth
);
775 UpdateUsage(context
, url
, growth
);
776 TouchDirectory(db
, file_info
.parent_id
);
777 context
->change_observers()->Notify(
778 &FileChangeObserver::OnRemoveDirectory
, MakeTuple(url
));
779 return base::File::FILE_OK
;
782 storage::ScopedFile
ObfuscatedFileUtil::CreateSnapshotFile(
783 FileSystemOperationContext
* context
,
784 const FileSystemURL
& url
,
785 base::File::Error
* error
,
786 base::File::Info
* file_info
,
787 base::FilePath
* platform_path
) {
788 // We're just returning the local file information.
789 *error
= GetFileInfo(context
, url
, file_info
, platform_path
);
790 if (*error
== base::File::FILE_OK
&& file_info
->is_directory
) {
791 *file_info
= base::File::Info();
792 *error
= base::File::FILE_ERROR_NOT_A_FILE
;
794 return storage::ScopedFile();
797 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
798 ObfuscatedFileUtil::CreateFileEnumerator(
799 FileSystemOperationContext
* context
,
800 const FileSystemURL
& root_url
,
802 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(root_url
, false);
804 return scoped_ptr
<AbstractFileEnumerator
>(new EmptyFileEnumerator());
806 return scoped_ptr
<AbstractFileEnumerator
>(
807 new ObfuscatedFileEnumerator(db
, context
, this, root_url
, recursive
));
810 bool ObfuscatedFileUtil::IsDirectoryEmpty(
811 FileSystemOperationContext
* context
,
812 const FileSystemURL
& url
) {
813 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
815 return true; // Not a great answer, but it's what others do.
817 if (!db
->GetFileWithPath(url
.path(), &file_id
))
818 return true; // Ditto.
820 if (!db
->GetFileInfo(file_id
, &file_info
)) {
822 // It's the root directory and the database hasn't been initialized yet.
825 if (!file_info
.is_directory())
827 std::vector
<FileId
> children
;
828 // TODO(ericu): This could easily be made faster with help from the database.
829 if (!db
->ListChildren(file_id
, &children
))
831 return children
.empty();
834 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOriginAndType(
836 const std::string
& type_string
,
838 base::File::Error
* error_code
) {
839 base::FilePath origin_dir
= GetDirectoryForOrigin(origin
, create
, error_code
);
840 if (origin_dir
.empty())
841 return base::FilePath();
842 if (type_string
.empty())
844 base::FilePath path
= origin_dir
.AppendASCII(type_string
);
845 base::File::Error error
= base::File::FILE_OK
;
846 if (!base::DirectoryExists(path
) &&
847 (!create
|| !base::CreateDirectory(path
))) {
849 base::File::FILE_ERROR_FAILED
:
850 base::File::FILE_ERROR_NOT_FOUND
;
858 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
860 const std::string
& type_string
) {
861 DestroyDirectoryDatabase(origin
, type_string
);
863 const base::FilePath origin_path
= GetDirectoryForOrigin(origin
, false, NULL
);
864 if (!type_string
.empty()) {
865 // Delete the filesystem type directory.
866 base::File::Error error
= base::File::FILE_OK
;
867 const base::FilePath origin_type_path
=
868 GetDirectoryForOriginAndType(origin
, type_string
, false, &error
);
869 if (error
== base::File::FILE_ERROR_FAILED
)
871 if (error
== base::File::FILE_OK
&&
872 !origin_type_path
.empty() &&
873 !base::DeleteFile(origin_type_path
, true /* recursive */)) {
877 // At this point we are sure we had successfully deleted the origin/type
878 // directory (i.e. we're ready to just return true).
879 // See if we have other directories in this origin directory.
880 for (std::set
<std::string
>::iterator iter
= known_type_strings_
.begin();
881 iter
!= known_type_strings_
.end();
883 if (*iter
== type_string
)
885 if (base::DirectoryExists(origin_path
.AppendASCII(*iter
))) {
886 // Other type's directory exists; just return true here.
892 // No other directories seem exist. Try deleting the entire origin directory.
893 InitOriginDatabase(origin
, false);
894 if (origin_database_
) {
895 origin_database_
->RemovePathForOrigin(
896 storage::GetIdentifierFromOrigin(origin
));
898 return base::DeleteFile(origin_path
, true /* recursive */);
901 ObfuscatedFileUtil::AbstractOriginEnumerator
*
902 ObfuscatedFileUtil::CreateOriginEnumerator() {
903 std::vector
<SandboxOriginDatabase::OriginRecord
> origins
;
905 InitOriginDatabase(GURL(), false);
906 return new ObfuscatedOriginEnumerator(
907 origin_database_
.get(), file_system_directory_
);
910 void ObfuscatedFileUtil::DestroyDirectoryDatabase(
912 const std::string
& type_string
) {
913 // If |type_string| is empty, delete all filesystem types under |origin|.
914 const std::string key_prefix
= GetDirectoryDatabaseKey(origin
, type_string
);
915 for (DirectoryMap::iterator iter
= directories_
.lower_bound(key_prefix
);
916 iter
!= directories_
.end();) {
917 if (!StartsWithASCII(iter
->first
, key_prefix
, true))
919 DCHECK(type_string
.empty() || iter
->first
== key_prefix
);
920 scoped_ptr
<SandboxDirectoryDatabase
> database(iter
->second
);
921 directories_
.erase(iter
++);
923 // Continue to destroy databases even if it failed because it doesn't affect
925 database
->DestroyDatabase();
930 int64
ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath
& path
) {
931 return UsageForPath(VirtualPath::BaseName(path
).value().size());
934 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
935 const std::vector
<std::string
>& type_strings_to_prepopulate
) {
936 SandboxPrioritizedOriginDatabase
database(file_system_directory_
,
938 std::string origin_string
= database
.GetPrimaryOrigin();
939 if (origin_string
.empty() || !database
.HasOriginPath(origin_string
))
941 const GURL origin
= storage::GetOriginFromIdentifier(origin_string
);
943 // Prepopulate the directory database(s) if and only if this instance
944 // has primary origin and the directory database is already there.
945 for (size_t i
= 0; i
< type_strings_to_prepopulate
.size(); ++i
) {
946 const std::string type_string
= type_strings_to_prepopulate
[i
];
947 // Only handles known types.
948 if (!ContainsKey(known_type_strings_
, type_string
))
950 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
951 base::FilePath path
= GetDirectoryForOriginAndType(
952 origin
, type_string
, false, &error
);
953 if (error
!= base::File::FILE_OK
)
955 scoped_ptr
<SandboxDirectoryDatabase
> db(
956 new SandboxDirectoryDatabase(path
, env_override_
));
957 if (db
->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION
)) {
958 directories_
[GetDirectoryDatabaseKey(origin
, type_string
)] = db
.release();
960 // Don't populate more than one database, as it may rather hurt
967 base::FilePath
ObfuscatedFileUtil::GetDirectoryForURL(
968 const FileSystemURL
& url
,
970 base::File::Error
* error_code
) {
971 return GetDirectoryForOriginAndType(
972 url
.origin(), CallGetTypeStringForURL(url
), create
, error_code
);
975 std::string
ObfuscatedFileUtil::CallGetTypeStringForURL(
976 const FileSystemURL
& url
) {
977 DCHECK(!get_type_string_for_url_
.is_null());
978 return get_type_string_for_url_
.Run(url
);
981 base::File::Error
ObfuscatedFileUtil::GetFileInfoInternal(
982 SandboxDirectoryDatabase
* db
,
983 FileSystemOperationContext
* context
,
984 const FileSystemURL
& url
,
986 FileInfo
* local_info
,
987 base::File::Info
* file_info
,
988 base::FilePath
* platform_file_path
) {
992 DCHECK(platform_file_path
);
994 if (!db
->GetFileInfo(file_id
, local_info
)) {
996 return base::File::FILE_ERROR_FAILED
;
999 if (local_info
->is_directory()) {
1000 file_info
->size
= 0;
1001 file_info
->is_directory
= true;
1002 file_info
->is_symbolic_link
= false;
1003 file_info
->last_modified
= local_info
->modification_time
;
1004 *platform_file_path
= base::FilePath();
1005 // We don't fill in ctime or atime.
1006 return base::File::FILE_OK
;
1008 if (local_info
->data_path
.empty())
1009 return base::File::FILE_ERROR_INVALID_OPERATION
;
1010 base::FilePath local_path
= DataPathToLocalPath(url
, local_info
->data_path
);
1011 base::File::Error error
= NativeFileUtil::GetFileInfo(
1012 local_path
, file_info
);
1013 // We should not follow symbolic links in sandboxed file system.
1014 if (base::IsLink(local_path
)) {
1015 LOG(WARNING
) << "Found a symbolic file.";
1016 error
= base::File::FILE_ERROR_NOT_FOUND
;
1018 if (error
== base::File::FILE_OK
) {
1019 *platform_file_path
= local_path
;
1020 } else if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1021 LOG(WARNING
) << "Lost a backing file.";
1022 InvalidateUsageCache(context
, url
.origin(), url
.type());
1023 if (!db
->RemoveFileInfo(file_id
))
1024 return base::File::FILE_ERROR_FAILED
;
1029 base::File
ObfuscatedFileUtil::CreateAndOpenFile(
1030 FileSystemOperationContext
* context
,
1031 const FileSystemURL
& dest_url
,
1032 FileInfo
* dest_file_info
, int file_flags
) {
1033 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1035 base::FilePath root
, dest_local_path
;
1036 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1038 if (error
!= base::File::FILE_OK
)
1039 return base::File(error
);
1041 if (base::PathExists(dest_local_path
)) {
1042 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1043 return base::File(base::File::FILE_ERROR_FAILED
);
1044 LOG(WARNING
) << "A stray file detected";
1045 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1048 base::File file
= NativeFileUtil::CreateOrOpen(dest_local_path
, file_flags
);
1049 if (!file
.IsValid())
1052 if (!file
.created()) {
1054 base::DeleteFile(dest_local_path
, false /* recursive */);
1055 return base::File(base::File::FILE_ERROR_FAILED
);
1058 error
= CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1059 if (error
!= base::File::FILE_OK
) {
1061 base::DeleteFile(dest_local_path
, false /* recursive */);
1062 return base::File(error
);
1068 base::File::Error
ObfuscatedFileUtil::CreateFile(
1069 FileSystemOperationContext
* context
,
1070 const base::FilePath
& src_file_path
,
1071 const FileSystemURL
& dest_url
,
1072 FileInfo
* dest_file_info
) {
1073 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1075 base::FilePath root
, dest_local_path
;
1076 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1078 if (error
!= base::File::FILE_OK
)
1081 bool created
= false;
1082 if (src_file_path
.empty()) {
1083 if (base::PathExists(dest_local_path
)) {
1084 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1085 return base::File::FILE_ERROR_FAILED
;
1086 LOG(WARNING
) << "A stray file detected";
1087 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1090 error
= NativeFileUtil::EnsureFileExists(dest_local_path
, &created
);
1092 error
= NativeFileUtil::CopyOrMoveFile(
1095 FileSystemOperation::OPTION_NONE
,
1096 storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
1100 if (error
!= base::File::FILE_OK
)
1103 return base::File::FILE_ERROR_FAILED
;
1105 return CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1108 base::File::Error
ObfuscatedFileUtil::CommitCreateFile(
1109 const base::FilePath
& root
,
1110 const base::FilePath
& local_path
,
1111 SandboxDirectoryDatabase
* db
,
1112 FileInfo
* dest_file_info
) {
1113 // This removes the root, including the trailing slash, leaving a relative
1115 dest_file_info
->data_path
= base::FilePath(
1116 local_path
.value().substr(root
.value().length() + 1));
1119 base::File::Error error
= db
->AddFileInfo(*dest_file_info
, &file_id
);
1120 if (error
!= base::File::FILE_OK
)
1123 TouchDirectory(db
, dest_file_info
->parent_id
);
1124 return base::File::FILE_OK
;
1127 base::FilePath
ObfuscatedFileUtil::DataPathToLocalPath(
1128 const FileSystemURL
& url
, const base::FilePath
& data_path
) {
1129 base::File::Error error
= base::File::FILE_OK
;
1130 base::FilePath root
= GetDirectoryForURL(url
, false, &error
);
1131 if (error
!= base::File::FILE_OK
)
1132 return base::FilePath();
1133 return root
.Append(data_path
);
1136 std::string
ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1137 const GURL
& origin
, const std::string
& type_string
) {
1138 // For isolated origin we just use a type string as a key.
1139 return storage::GetIdentifierFromOrigin(origin
) +
1140 kDirectoryDatabaseKeySeparator
+ type_string
;
1143 // TODO(ericu): How to do the whole validation-without-creation thing?
1144 // We may not have quota even to create the database.
1145 // Ah, in that case don't even get here?
1146 // Still doesn't answer the quota issue, though.
1147 SandboxDirectoryDatabase
* ObfuscatedFileUtil::GetDirectoryDatabase(
1148 const FileSystemURL
& url
, bool create
) {
1149 std::string key
= GetDirectoryDatabaseKey(
1150 url
.origin(), CallGetTypeStringForURL(url
));
1154 DirectoryMap::iterator iter
= directories_
.find(key
);
1155 if (iter
!= directories_
.end()) {
1157 return iter
->second
;
1160 base::File::Error error
= base::File::FILE_OK
;
1161 base::FilePath path
= GetDirectoryForURL(url
, create
, &error
);
1162 if (error
!= base::File::FILE_OK
) {
1163 LOG(WARNING
) << "Failed to get origin+type directory: "
1164 << url
.DebugString() << " error:" << error
;
1168 SandboxDirectoryDatabase
* database
=
1169 new SandboxDirectoryDatabase(path
, env_override_
);
1170 directories_
[key
] = database
;
1174 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOrigin(
1175 const GURL
& origin
, bool create
, base::File::Error
* error_code
) {
1176 if (!InitOriginDatabase(origin
, create
)) {
1178 *error_code
= create
?
1179 base::File::FILE_ERROR_FAILED
:
1180 base::File::FILE_ERROR_NOT_FOUND
;
1182 return base::FilePath();
1184 base::FilePath directory_name
;
1185 std::string id
= storage::GetIdentifierFromOrigin(origin
);
1187 bool exists_in_db
= origin_database_
->HasOriginPath(id
);
1188 if (!exists_in_db
&& !create
) {
1190 *error_code
= base::File::FILE_ERROR_NOT_FOUND
;
1191 return base::FilePath();
1193 if (!origin_database_
->GetPathForOrigin(id
, &directory_name
)) {
1195 *error_code
= base::File::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::File::FILE_ERROR_FAILED
;
1205 return base::FilePath();
1207 exists_in_fs
= false;
1210 if (!exists_in_fs
) {
1211 if (!create
|| !base::CreateDirectory(path
)) {
1213 *error_code
= create
?
1214 base::File::FILE_ERROR_FAILED
:
1215 base::File::FILE_ERROR_NOT_FOUND
;
1216 return base::FilePath();
1221 *error_code
= base::File::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 (!base::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_
,
1272 origin_database_
.reset(prioritized_origin_database
);
1274 if (origin_hint
.is_empty() || !HasIsolatedStorage(origin_hint
))
1277 const std::string isolated_origin_string
=
1278 storage::GetIdentifierFromOrigin(origin_hint
);
1280 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1281 base::FilePath isolated_origin_dir
= file_system_directory_
.Append(
1282 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory
);
1283 if (base::DirectoryExists(isolated_origin_dir
) &&
1284 prioritized_origin_database
->GetSandboxOriginDatabase()) {
1285 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1286 isolated_origin_string
,
1287 file_system_directory_
,
1288 prioritized_origin_database
->GetSandboxOriginDatabase());
1291 prioritized_origin_database
->InitializePrimaryOrigin(
1292 isolated_origin_string
);
1297 base::File::Error
ObfuscatedFileUtil::GenerateNewLocalPath(
1298 SandboxDirectoryDatabase
* db
,
1299 FileSystemOperationContext
* context
,
1300 const FileSystemURL
& url
,
1301 base::FilePath
* root
,
1302 base::FilePath
* local_path
) {
1305 if (!db
|| !db
->GetNextInteger(&number
))
1306 return base::File::FILE_ERROR_FAILED
;
1308 base::File::Error error
= base::File::FILE_OK
;
1309 *root
= GetDirectoryForURL(url
, false, &error
);
1310 if (error
!= base::File::FILE_OK
)
1313 // We use the third- and fourth-to-last digits as the directory.
1314 int64 directory_number
= number
% 10000 / 100;
1315 base::FilePath new_local_path
= root
->AppendASCII(
1316 base::StringPrintf("%02" PRId64
, directory_number
));
1318 error
= NativeFileUtil::CreateDirectory(
1319 new_local_path
, false /* exclusive */, false /* recursive */);
1320 if (error
!= base::File::FILE_OK
)
1324 new_local_path
.AppendASCII(base::StringPrintf("%08" PRId64
, number
));
1325 return base::File::FILE_OK
;
1328 base::File
ObfuscatedFileUtil::CreateOrOpenInternal(
1329 FileSystemOperationContext
* context
,
1330 const FileSystemURL
& url
, int file_flags
) {
1331 DCHECK(!(file_flags
& (base::File::FLAG_DELETE_ON_CLOSE
|
1332 base::File::FLAG_HIDDEN
| base::File::FLAG_EXCLUSIVE_READ
|
1333 base::File::FLAG_EXCLUSIVE_WRITE
)));
1334 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
1336 return base::File(base::File::FILE_ERROR_FAILED
);
1338 if (!db
->GetFileWithPath(url
.path(), &file_id
)) {
1339 // The file doesn't exist.
1340 if (!(file_flags
& (base::File::FLAG_CREATE
|
1341 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_OPEN_ALWAYS
))) {
1342 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1345 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
1346 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1348 InitFileInfo(&file_info
, parent_id
,
1349 VirtualPath::BaseName(url
.path()).value());
1351 int64 growth
= UsageForPath(file_info
.name
.size());
1352 if (!AllocateQuota(context
, growth
))
1353 return base::File(base::File::FILE_ERROR_NO_SPACE
);
1354 base::File file
= CreateAndOpenFile(context
, url
, &file_info
, file_flags
);
1355 if (file
.IsValid()) {
1356 UpdateUsage(context
, url
, growth
);
1357 context
->change_observers()->Notify(
1358 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
1363 if (file_flags
& base::File::FLAG_CREATE
)
1364 return base::File(base::File::FILE_ERROR_EXISTS
);
1366 base::File::Info platform_file_info
;
1367 base::FilePath local_path
;
1369 base::File::Error error
= GetFileInfoInternal(
1370 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
1371 if (error
!= base::File::FILE_OK
)
1372 return base::File(error
);
1373 if (file_info
.is_directory())
1374 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
1377 if (file_flags
& (base::File::FLAG_CREATE_ALWAYS
|
1378 base::File::FLAG_OPEN_TRUNCATED
)) {
1379 // The file exists and we're truncating.
1380 delta
= -platform_file_info
.size
;
1381 AllocateQuota(context
, delta
);
1384 base::File file
= NativeFileUtil::CreateOrOpen(local_path
, file_flags
);
1385 if (!file
.IsValid()) {
1386 error
= file
.error_details();
1387 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1388 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1389 // TODO(tzik): Delete database entry after ensuring the file lost.
1390 InvalidateUsageCache(context
, url
.origin(), url
.type());
1391 LOG(WARNING
) << "Lost a backing file.";
1392 return base::File(base::File::FILE_ERROR_FAILED
);
1397 // If truncating we need to update the usage.
1399 UpdateUsage(context
, url
, delta
);
1400 context
->change_observers()->Notify(
1401 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
1406 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL
& origin
) {
1407 return special_storage_policy_
.get() &&
1408 special_storage_policy_
->HasIsolatedStorage(origin
);
1411 } // namespace storage