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"
9 #include "base/files/file_util.h"
10 #include "base/format_macros.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/sys_string_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "storage/browser/fileapi/file_observers.h"
22 #include "storage/browser/fileapi/file_system_context.h"
23 #include "storage/browser/fileapi/file_system_operation_context.h"
24 #include "storage/browser/fileapi/native_file_util.h"
25 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
26 #include "storage/browser/fileapi/sandbox_isolated_origin_database.h"
27 #include "storage/browser/fileapi/sandbox_origin_database.h"
28 #include "storage/browser/fileapi/sandbox_prioritized_origin_database.h"
29 #include "storage/browser/fileapi/timed_task_helper.h"
30 #include "storage/browser/quota/quota_manager.h"
31 #include "storage/common/database/database_identifier.h"
32 #include "storage/common/fileapi/file_system_util.h"
35 // Example of various paths:
36 // void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
37 // base::FilePath virtual_path = url.path();
38 // base::FilePath local_path = GetLocalFilePath(url);
40 // NativeFileUtil::DoSomething(local_path);
41 // file_util::DoAnother(local_path);
48 typedef SandboxDirectoryDatabase::FileId FileId
;
49 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
52 SandboxDirectoryDatabase::FileInfo
* file_info
,
53 SandboxDirectoryDatabase::FileId parent_id
,
54 const base::FilePath::StringType
& file_name
) {
56 file_info
->parent_id
= parent_id
;
57 file_info
->name
= file_name
;
60 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of
61 // path storage under Linux. It's not clear if that will differ on Windows, on
62 // which base::FilePath uses wide chars [since they're converted to UTF-8 for
63 // storage anyway], but as long as the cost is high enough that one can't cheat
64 // on quota by storing data in paths, it doesn't need to be all that accurate.
65 const int64 kPathCreationQuotaCost
= 146; // Bytes per inode, basically.
66 const int64 kPathByteQuotaCost
= 2; // Bytes per byte of path length in UTF-8.
68 const char kDirectoryDatabaseKeySeparator
= ' ';
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() == storage::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
, base::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 class ObfuscatedFileEnumerator
109 : public FileSystemFileUtil::AbstractFileEnumerator
{
111 ObfuscatedFileEnumerator(
112 SandboxDirectoryDatabase
* db
,
113 FileSystemOperationContext
* context
,
114 ObfuscatedFileUtil
* obfuscated_file_util
,
115 const FileSystemURL
& root_url
,
119 obfuscated_file_util_(obfuscated_file_util
),
121 recursive_(recursive
),
122 current_file_id_(0) {
123 base::FilePath root_virtual_path
= root_url
.path();
126 if (!db_
->GetFileWithPath(root_virtual_path
, &file_id
))
129 FileRecord record
= { file_id
, root_virtual_path
};
130 recurse_queue_
.push(record
);
133 ~ObfuscatedFileEnumerator() override
{}
135 base::FilePath
Next() override
{
136 ProcessRecurseQueue();
137 if (display_stack_
.empty())
138 return base::FilePath();
140 current_file_id_
= display_stack_
.back();
141 display_stack_
.pop_back();
144 base::FilePath platform_file_path
;
145 base::File::Error error
=
146 obfuscated_file_util_
->GetFileInfoInternal(
147 db_
, context_
, root_url_
, current_file_id_
,
148 &file_info
, ¤t_platform_file_info_
, &platform_file_path
);
149 if (error
!= base::File::FILE_OK
)
152 base::FilePath virtual_path
=
153 current_parent_virtual_path_
.Append(file_info
.name
);
154 if (recursive_
&& file_info
.is_directory()) {
155 FileRecord record
= { current_file_id_
, virtual_path
};
156 recurse_queue_
.push(record
);
161 int64
Size() override
{ return current_platform_file_info_
.size
; }
163 base::Time
LastModifiedTime() override
{
164 return current_platform_file_info_
.last_modified
;
167 bool IsDirectory() override
{
168 return current_platform_file_info_
.is_directory
;
172 typedef SandboxDirectoryDatabase::FileId FileId
;
173 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
177 base::FilePath virtual_path
;
180 void ProcessRecurseQueue() {
181 while (display_stack_
.empty() && !recurse_queue_
.empty()) {
182 FileRecord entry
= recurse_queue_
.front();
183 recurse_queue_
.pop();
184 if (!db_
->ListChildren(entry
.file_id
, &display_stack_
)) {
185 display_stack_
.clear();
188 current_parent_virtual_path_
= entry
.virtual_path
;
192 SandboxDirectoryDatabase
* db_
;
193 FileSystemOperationContext
* context_
;
194 ObfuscatedFileUtil
* obfuscated_file_util_
;
195 FileSystemURL root_url_
;
198 std::queue
<FileRecord
> recurse_queue_
;
199 std::vector
<FileId
> display_stack_
;
200 base::FilePath current_parent_virtual_path_
;
202 FileId current_file_id_
;
203 base::File::Info current_platform_file_info_
;
206 class ObfuscatedOriginEnumerator
207 : public ObfuscatedFileUtil::AbstractOriginEnumerator
{
209 typedef SandboxOriginDatabase::OriginRecord OriginRecord
;
210 ObfuscatedOriginEnumerator(
211 SandboxOriginDatabaseInterface
* origin_database
,
212 const base::FilePath
& base_file_path
)
213 : base_file_path_(base_file_path
) {
215 origin_database
->ListAllOrigins(&origins_
);
218 ~ObfuscatedOriginEnumerator() override
{}
220 // Returns the next origin. Returns empty if there are no more origins.
221 GURL
Next() override
{
223 if (!origins_
.empty()) {
224 record
= origins_
.back();
228 return storage::GetOriginFromIdentifier(record
.origin
);
231 // Returns the current origin's information.
232 bool HasTypeDirectory(const std::string
& type_string
) const override
{
233 if (current_
.path
.empty())
235 if (type_string
.empty()) {
239 base::FilePath path
=
240 base_file_path_
.Append(current_
.path
).AppendASCII(type_string
);
241 return base::DirectoryExists(path
);
245 std::vector
<OriginRecord
> origins_
;
246 OriginRecord current_
;
247 base::FilePath base_file_path_
;
250 ObfuscatedFileUtil::ObfuscatedFileUtil(
251 storage::SpecialStoragePolicy
* special_storage_policy
,
252 const base::FilePath
& file_system_directory
,
253 leveldb::Env
* env_override
,
254 base::SequencedTaskRunner
* file_task_runner
,
255 const GetTypeStringForURLCallback
& get_type_string_for_url
,
256 const std::set
<std::string
>& known_type_strings
,
257 SandboxFileSystemBackendDelegate
* sandbox_delegate
)
258 : special_storage_policy_(special_storage_policy
),
259 file_system_directory_(file_system_directory
),
260 env_override_(env_override
),
261 db_flush_delay_seconds_(10 * 60), // 10 mins.
262 file_task_runner_(file_task_runner
),
263 get_type_string_for_url_(get_type_string_for_url
),
264 known_type_strings_(known_type_strings
),
265 sandbox_delegate_(sandbox_delegate
) {
268 ObfuscatedFileUtil::~ObfuscatedFileUtil() {
272 base::File
ObfuscatedFileUtil::CreateOrOpen(
273 FileSystemOperationContext
* context
,
274 const FileSystemURL
& url
, int file_flags
) {
275 base::File file
= CreateOrOpenInternal(context
, url
, file_flags
);
276 if (file
.IsValid() && file_flags
& base::File::FLAG_WRITE
&&
277 context
->quota_limit_type() == storage::kQuotaLimitTypeUnlimited
&&
279 sandbox_delegate_
->StickyInvalidateUsageCache(url
.origin(), url
.type());
284 base::File::Error
ObfuscatedFileUtil::EnsureFileExists(
285 FileSystemOperationContext
* context
,
286 const FileSystemURL
& url
,
288 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
290 return base::File::FILE_ERROR_FAILED
;
293 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
295 if (!db
->GetFileInfo(file_id
, &file_info
)) {
297 return base::File::FILE_ERROR_FAILED
;
299 if (file_info
.is_directory())
300 return base::File::FILE_ERROR_NOT_A_FILE
;
303 return base::File::FILE_OK
;
306 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
307 return base::File::FILE_ERROR_NOT_FOUND
;
310 InitFileInfo(&file_info
, parent_id
,
311 VirtualPath::BaseName(url
.path()).value());
313 int64 growth
= UsageForPath(file_info
.name
.size());
314 if (!AllocateQuota(context
, growth
))
315 return base::File::FILE_ERROR_NO_SPACE
;
316 base::File::Error error
= CreateFile(context
, base::FilePath(), url
,
318 if (created
&& base::File::FILE_OK
== error
) {
320 UpdateUsage(context
, url
, growth
);
321 context
->change_observers()->Notify(
322 &FileChangeObserver::OnCreateFile
, base::MakeTuple(url
));
327 base::File::Error
ObfuscatedFileUtil::CreateDirectory(
328 FileSystemOperationContext
* context
,
329 const FileSystemURL
& url
,
332 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
334 return base::File::FILE_ERROR_FAILED
;
337 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
340 return base::File::FILE_ERROR_EXISTS
;
341 if (!db
->GetFileInfo(file_id
, &file_info
)) {
343 return base::File::FILE_ERROR_FAILED
;
345 if (!file_info
.is_directory())
346 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
347 return base::File::FILE_OK
;
350 std::vector
<base::FilePath::StringType
> components
;
351 VirtualPath::GetComponents(url
.path(), &components
);
352 FileId parent_id
= 0;
354 for (index
= 0; index
< components
.size(); ++index
) {
355 base::FilePath::StringType name
= components
[index
];
356 if (name
== FILE_PATH_LITERAL("/"))
358 if (!db
->GetChildWithName(parent_id
, name
, &parent_id
))
361 if (!db
->IsDirectory(parent_id
))
362 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
363 if (!recursive
&& components
.size() - index
> 1)
364 return base::File::FILE_ERROR_NOT_FOUND
;
366 for (; index
< components
.size(); ++index
) {
368 file_info
.name
= components
[index
];
369 if (file_info
.name
== FILE_PATH_LITERAL("/"))
371 file_info
.modification_time
= base::Time::Now();
372 file_info
.parent_id
= parent_id
;
373 int64 growth
= UsageForPath(file_info
.name
.size());
374 if (!AllocateQuota(context
, growth
))
375 return base::File::FILE_ERROR_NO_SPACE
;
376 base::File::Error error
= db
->AddFileInfo(file_info
, &parent_id
);
377 if (error
!= base::File::FILE_OK
)
379 UpdateUsage(context
, url
, growth
);
380 context
->change_observers()->Notify(
381 &FileChangeObserver::OnCreateDirectory
, base::MakeTuple(url
));
384 TouchDirectory(db
, file_info
.parent_id
);
387 return base::File::FILE_OK
;
390 base::File::Error
ObfuscatedFileUtil::GetFileInfo(
391 FileSystemOperationContext
* context
,
392 const FileSystemURL
& url
,
393 base::File::Info
* file_info
,
394 base::FilePath
* platform_file_path
) {
395 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
397 return base::File::FILE_ERROR_NOT_FOUND
;
399 if (!db
->GetFileWithPath(url
.path(), &file_id
))
400 return base::File::FILE_ERROR_NOT_FOUND
;
402 return GetFileInfoInternal(db
, context
, url
,
403 file_id
, &local_info
,
404 file_info
, platform_file_path
);
407 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
408 ObfuscatedFileUtil::CreateFileEnumerator(
409 FileSystemOperationContext
* context
,
410 const FileSystemURL
& root_url
) {
411 return CreateFileEnumerator(context
, root_url
, false /* recursive */);
414 base::File::Error
ObfuscatedFileUtil::GetLocalFilePath(
415 FileSystemOperationContext
* context
,
416 const FileSystemURL
& url
,
417 base::FilePath
* local_path
) {
418 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
420 return base::File::FILE_ERROR_NOT_FOUND
;
422 if (!db
->GetFileWithPath(url
.path(), &file_id
))
423 return base::File::FILE_ERROR_NOT_FOUND
;
425 if (!db
->GetFileInfo(file_id
, &file_info
) || file_info
.is_directory()) {
427 // Directories have no local file path.
428 return base::File::FILE_ERROR_NOT_FOUND
;
430 *local_path
= DataPathToLocalPath(url
, file_info
.data_path
);
432 if (local_path
->empty())
433 return base::File::FILE_ERROR_NOT_FOUND
;
434 return base::File::FILE_OK
;
437 base::File::Error
ObfuscatedFileUtil::Touch(
438 FileSystemOperationContext
* context
,
439 const FileSystemURL
& url
,
440 const base::Time
& last_access_time
,
441 const base::Time
& last_modified_time
) {
442 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
444 return base::File::FILE_ERROR_NOT_FOUND
;
446 if (!db
->GetFileWithPath(url
.path(), &file_id
))
447 return base::File::FILE_ERROR_NOT_FOUND
;
450 if (!db
->GetFileInfo(file_id
, &file_info
)) {
452 return base::File::FILE_ERROR_FAILED
;
454 if (file_info
.is_directory()) {
455 if (!db
->UpdateModificationTime(file_id
, last_modified_time
))
456 return base::File::FILE_ERROR_FAILED
;
457 return base::File::FILE_OK
;
459 return NativeFileUtil::Touch(
460 DataPathToLocalPath(url
, file_info
.data_path
),
461 last_access_time
, last_modified_time
);
464 base::File::Error
ObfuscatedFileUtil::Truncate(
465 FileSystemOperationContext
* context
,
466 const FileSystemURL
& url
,
468 base::File::Info file_info
;
469 base::FilePath local_path
;
470 base::File::Error error
=
471 GetFileInfo(context
, url
, &file_info
, &local_path
);
472 if (error
!= base::File::FILE_OK
)
475 int64 growth
= length
- file_info
.size
;
476 if (!AllocateQuota(context
, growth
))
477 return base::File::FILE_ERROR_NO_SPACE
;
478 error
= NativeFileUtil::Truncate(local_path
, length
);
479 if (error
== base::File::FILE_OK
) {
480 UpdateUsage(context
, url
, growth
);
481 context
->change_observers()->Notify(
482 &FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
487 base::File::Error
ObfuscatedFileUtil::CopyOrMoveFile(
488 FileSystemOperationContext
* context
,
489 const FileSystemURL
& src_url
,
490 const FileSystemURL
& dest_url
,
491 CopyOrMoveOption option
,
493 // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
494 DCHECK(src_url
.origin() == dest_url
.origin());
495 DCHECK(src_url
.type() == dest_url
.type());
497 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(src_url
, true);
499 return base::File::FILE_ERROR_FAILED
;
502 if (!db
->GetFileWithPath(src_url
.path(), &src_file_id
))
503 return base::File::FILE_ERROR_NOT_FOUND
;
506 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
509 FileInfo src_file_info
;
510 base::File::Info src_platform_file_info
;
511 base::FilePath src_local_path
;
512 base::File::Error error
= GetFileInfoInternal(
513 db
, context
, src_url
, src_file_id
,
514 &src_file_info
, &src_platform_file_info
, &src_local_path
);
515 if (error
!= base::File::FILE_OK
)
517 if (src_file_info
.is_directory())
518 return base::File::FILE_ERROR_NOT_A_FILE
;
520 FileInfo dest_file_info
;
521 base::File::Info dest_platform_file_info
; // overwrite case only
522 base::FilePath dest_local_path
; // overwrite case only
524 base::File::Error error
= GetFileInfoInternal(
525 db
, context
, dest_url
, dest_file_id
,
526 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
527 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
528 overwrite
= false; // fallback to non-overwrite case
529 else if (error
!= base::File::FILE_OK
)
531 else if (dest_file_info
.is_directory())
532 return base::File::FILE_ERROR_INVALID_OPERATION
;
535 FileId dest_parent_id
;
536 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
538 return base::File::FILE_ERROR_NOT_FOUND
;
541 dest_file_info
= src_file_info
;
542 dest_file_info
.parent_id
= dest_parent_id
;
543 dest_file_info
.name
=
544 VirtualPath::BaseName(dest_url
.path()).value();
549 growth
+= src_platform_file_info
.size
;
551 growth
-= UsageForPath(src_file_info
.name
.size());
553 growth
-= dest_platform_file_info
.size
;
555 growth
+= UsageForPath(dest_file_info
.name
.size());
556 if (!AllocateQuota(context
, growth
))
557 return base::File::FILE_ERROR_NO_SPACE
;
560 * Copy-with-overwrite
561 * Just overwrite data file
562 * Copy-without-overwrite
564 * Create new metadata pointing to new backing file.
565 * Move-with-overwrite
567 * Remove source entry.
568 * Point target entry to source entry's backing file.
569 * Delete target entry's old backing file
570 * Move-without-overwrite
571 * Just update metadata
573 error
= base::File::FILE_ERROR_FAILED
;
576 error
= NativeFileUtil::CopyOrMoveFile(
580 storage::NativeFileUtil::CopyOrMoveModeForDestination(
581 dest_url
, true /* copy */));
582 } else { // non-overwrite
583 error
= CreateFile(context
, src_local_path
, dest_url
, &dest_file_info
);
587 if (db
->OverwritingMoveFile(src_file_id
, dest_file_id
)) {
588 if (base::File::FILE_OK
!=
589 NativeFileUtil::DeleteFile(dest_local_path
))
590 LOG(WARNING
) << "Leaked a backing file.";
591 error
= base::File::FILE_OK
;
593 error
= base::File::FILE_ERROR_FAILED
;
595 } else { // non-overwrite
596 if (db
->UpdateFileInfo(src_file_id
, dest_file_info
))
597 error
= base::File::FILE_OK
;
599 error
= base::File::FILE_ERROR_FAILED
;
603 if (error
!= base::File::FILE_OK
)
607 context
->change_observers()->Notify(
608 &FileChangeObserver::OnModifyFile
,
609 base::MakeTuple(dest_url
));
611 context
->change_observers()->Notify(
612 &FileChangeObserver::OnCreateFileFrom
,
613 base::MakeTuple(dest_url
, src_url
));
617 context
->change_observers()->Notify(
618 &FileChangeObserver::OnRemoveFile
, base::MakeTuple(src_url
));
619 TouchDirectory(db
, src_file_info
.parent_id
);
622 TouchDirectory(db
, dest_file_info
.parent_id
);
624 UpdateUsage(context
, dest_url
, growth
);
628 base::File::Error
ObfuscatedFileUtil::CopyInForeignFile(
629 FileSystemOperationContext
* context
,
630 const base::FilePath
& src_file_path
,
631 const FileSystemURL
& dest_url
) {
632 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
634 return base::File::FILE_ERROR_FAILED
;
636 base::File::Info src_platform_file_info
;
637 if (!base::GetFileInfo(src_file_path
, &src_platform_file_info
))
638 return base::File::FILE_ERROR_NOT_FOUND
;
641 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
644 FileInfo dest_file_info
;
645 base::File::Info dest_platform_file_info
; // overwrite case only
647 base::FilePath dest_local_path
;
648 base::File::Error error
= GetFileInfoInternal(
649 db
, context
, dest_url
, dest_file_id
,
650 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
651 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
652 overwrite
= false; // fallback to non-overwrite case
653 else if (error
!= base::File::FILE_OK
)
655 else if (dest_file_info
.is_directory())
656 return base::File::FILE_ERROR_INVALID_OPERATION
;
659 FileId dest_parent_id
;
660 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
662 return base::File::FILE_ERROR_NOT_FOUND
;
664 if (!dest_file_info
.is_directory())
665 return base::File::FILE_ERROR_FAILED
;
666 InitFileInfo(&dest_file_info
, dest_parent_id
,
667 VirtualPath::BaseName(dest_url
.path()).value());
670 int64 growth
= src_platform_file_info
.size
;
672 growth
-= dest_platform_file_info
.size
;
674 growth
+= UsageForPath(dest_file_info
.name
.size());
675 if (!AllocateQuota(context
, growth
))
676 return base::File::FILE_ERROR_NO_SPACE
;
678 base::File::Error error
;
680 base::FilePath dest_local_path
=
681 DataPathToLocalPath(dest_url
, dest_file_info
.data_path
);
682 error
= NativeFileUtil::CopyOrMoveFile(
685 FileSystemOperation::OPTION_NONE
,
686 storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
689 error
= CreateFile(context
, src_file_path
, dest_url
, &dest_file_info
);
692 if (error
!= base::File::FILE_OK
)
696 context
->change_observers()->Notify(
697 &FileChangeObserver::OnModifyFile
, base::MakeTuple(dest_url
));
699 context
->change_observers()->Notify(
700 &FileChangeObserver::OnCreateFile
, base::MakeTuple(dest_url
));
703 UpdateUsage(context
, dest_url
, growth
);
704 TouchDirectory(db
, dest_file_info
.parent_id
);
705 return base::File::FILE_OK
;
708 base::File::Error
ObfuscatedFileUtil::DeleteFile(
709 FileSystemOperationContext
* context
,
710 const FileSystemURL
& url
) {
711 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
713 return base::File::FILE_ERROR_FAILED
;
715 if (!db
->GetFileWithPath(url
.path(), &file_id
))
716 return base::File::FILE_ERROR_NOT_FOUND
;
719 base::File::Info platform_file_info
;
720 base::FilePath local_path
;
721 base::File::Error error
= GetFileInfoInternal(
722 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
723 if (error
!= base::File::FILE_ERROR_NOT_FOUND
&&
724 error
!= base::File::FILE_OK
)
727 if (file_info
.is_directory())
728 return base::File::FILE_ERROR_NOT_A_FILE
;
730 int64 growth
= -UsageForPath(file_info
.name
.size()) - platform_file_info
.size
;
731 AllocateQuota(context
, growth
);
732 if (!db
->RemoveFileInfo(file_id
)) {
734 return base::File::FILE_ERROR_FAILED
;
736 UpdateUsage(context
, url
, growth
);
737 TouchDirectory(db
, file_info
.parent_id
);
739 context
->change_observers()->Notify(
740 &FileChangeObserver::OnRemoveFile
, base::MakeTuple(url
));
742 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
743 return base::File::FILE_OK
;
745 error
= NativeFileUtil::DeleteFile(local_path
);
746 if (base::File::FILE_OK
!= error
)
747 LOG(WARNING
) << "Leaked a backing file.";
748 return base::File::FILE_OK
;
751 base::File::Error
ObfuscatedFileUtil::DeleteDirectory(
752 FileSystemOperationContext
* context
,
753 const FileSystemURL
& url
) {
754 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
756 return base::File::FILE_ERROR_FAILED
;
759 if (!db
->GetFileWithPath(url
.path(), &file_id
))
760 return base::File::FILE_ERROR_NOT_FOUND
;
762 if (!db
->GetFileInfo(file_id
, &file_info
)) {
764 return base::File::FILE_ERROR_FAILED
;
766 if (!file_info
.is_directory())
767 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
768 if (!db
->RemoveFileInfo(file_id
))
769 return base::File::FILE_ERROR_NOT_EMPTY
;
770 int64 growth
= -UsageForPath(file_info
.name
.size());
771 AllocateQuota(context
, growth
);
772 UpdateUsage(context
, url
, growth
);
773 TouchDirectory(db
, file_info
.parent_id
);
774 context
->change_observers()->Notify(
775 &FileChangeObserver::OnRemoveDirectory
, base::MakeTuple(url
));
776 return base::File::FILE_OK
;
779 storage::ScopedFile
ObfuscatedFileUtil::CreateSnapshotFile(
780 FileSystemOperationContext
* context
,
781 const FileSystemURL
& url
,
782 base::File::Error
* error
,
783 base::File::Info
* file_info
,
784 base::FilePath
* platform_path
) {
785 // We're just returning the local file information.
786 *error
= GetFileInfo(context
, url
, file_info
, platform_path
);
787 if (*error
== base::File::FILE_OK
&& file_info
->is_directory
) {
788 *file_info
= base::File::Info();
789 *error
= base::File::FILE_ERROR_NOT_A_FILE
;
791 return storage::ScopedFile();
794 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
795 ObfuscatedFileUtil::CreateFileEnumerator(
796 FileSystemOperationContext
* context
,
797 const FileSystemURL
& root_url
,
799 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(root_url
, false);
801 return scoped_ptr
<AbstractFileEnumerator
>(new EmptyFileEnumerator());
803 return scoped_ptr
<AbstractFileEnumerator
>(
804 new ObfuscatedFileEnumerator(db
, context
, this, root_url
, recursive
));
807 bool ObfuscatedFileUtil::IsDirectoryEmpty(
808 FileSystemOperationContext
* context
,
809 const FileSystemURL
& url
) {
810 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
812 return true; // Not a great answer, but it's what others do.
814 if (!db
->GetFileWithPath(url
.path(), &file_id
))
815 return true; // Ditto.
817 if (!db
->GetFileInfo(file_id
, &file_info
)) {
819 // It's the root directory and the database hasn't been initialized yet.
822 if (!file_info
.is_directory())
824 std::vector
<FileId
> children
;
825 // TODO(ericu): This could easily be made faster with help from the database.
826 if (!db
->ListChildren(file_id
, &children
))
828 return children
.empty();
831 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOriginAndType(
833 const std::string
& type_string
,
835 base::File::Error
* error_code
) {
836 base::FilePath origin_dir
= GetDirectoryForOrigin(origin
, create
, error_code
);
837 if (origin_dir
.empty())
838 return base::FilePath();
839 if (type_string
.empty())
841 base::FilePath path
= origin_dir
.AppendASCII(type_string
);
842 base::File::Error error
= base::File::FILE_OK
;
843 if (!base::DirectoryExists(path
) &&
844 (!create
|| !base::CreateDirectory(path
))) {
846 base::File::FILE_ERROR_FAILED
:
847 base::File::FILE_ERROR_NOT_FOUND
;
855 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
857 const std::string
& type_string
) {
858 DestroyDirectoryDatabase(origin
, type_string
);
860 const base::FilePath origin_path
= GetDirectoryForOrigin(origin
, false, NULL
);
861 if (!type_string
.empty()) {
862 // Delete the filesystem type directory.
863 base::File::Error error
= base::File::FILE_OK
;
864 const base::FilePath origin_type_path
=
865 GetDirectoryForOriginAndType(origin
, type_string
, false, &error
);
866 if (error
== base::File::FILE_ERROR_FAILED
)
868 if (error
== base::File::FILE_OK
&&
869 !origin_type_path
.empty() &&
870 !base::DeleteFile(origin_type_path
, true /* recursive */)) {
874 // At this point we are sure we had successfully deleted the origin/type
875 // directory (i.e. we're ready to just return true).
876 // See if we have other directories in this origin directory.
877 for (std::set
<std::string
>::iterator iter
= known_type_strings_
.begin();
878 iter
!= known_type_strings_
.end();
880 if (*iter
== type_string
)
882 if (base::DirectoryExists(origin_path
.AppendASCII(*iter
))) {
883 // Other type's directory exists; just return true here.
889 // No other directories seem exist. Try deleting the entire origin directory.
890 InitOriginDatabase(origin
, false);
891 if (origin_database_
) {
892 origin_database_
->RemovePathForOrigin(
893 storage::GetIdentifierFromOrigin(origin
));
895 return base::DeleteFile(origin_path
, true /* recursive */);
898 void ObfuscatedFileUtil::CloseFileSystemForOriginAndType(
900 const std::string
& type_string
) {
901 const std::string key_prefix
= GetDirectoryDatabaseKey(origin
, type_string
);
902 for (DirectoryMap::iterator iter
= directories_
.lower_bound(key_prefix
);
903 iter
!= directories_
.end();) {
904 if (!StartsWithASCII(iter
->first
, key_prefix
, true))
906 DCHECK(type_string
.empty() || iter
->first
== key_prefix
);
907 scoped_ptr
<SandboxDirectoryDatabase
> database(iter
->second
);
908 directories_
.erase(iter
++);
912 ObfuscatedFileUtil::AbstractOriginEnumerator
*
913 ObfuscatedFileUtil::CreateOriginEnumerator() {
914 std::vector
<SandboxOriginDatabase::OriginRecord
> origins
;
916 InitOriginDatabase(GURL(), false);
917 return new ObfuscatedOriginEnumerator(
918 origin_database_
.get(), file_system_directory_
);
921 void ObfuscatedFileUtil::DestroyDirectoryDatabase(
923 const std::string
& type_string
) {
924 // If |type_string| is empty, delete all filesystem types under |origin|.
925 const std::string key_prefix
= GetDirectoryDatabaseKey(origin
, type_string
);
926 for (DirectoryMap::iterator iter
= directories_
.lower_bound(key_prefix
);
927 iter
!= directories_
.end();) {
928 if (!StartsWithASCII(iter
->first
, key_prefix
, true))
930 DCHECK(type_string
.empty() || iter
->first
== key_prefix
);
931 scoped_ptr
<SandboxDirectoryDatabase
> database(iter
->second
);
932 directories_
.erase(iter
++);
934 // Continue to destroy databases even if it failed because it doesn't affect
936 database
->DestroyDatabase();
941 int64
ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath
& path
) {
942 return UsageForPath(VirtualPath::BaseName(path
).value().size());
945 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
946 const std::vector
<std::string
>& type_strings_to_prepopulate
) {
947 SandboxPrioritizedOriginDatabase
database(file_system_directory_
,
949 std::string origin_string
= database
.GetPrimaryOrigin();
950 if (origin_string
.empty() || !database
.HasOriginPath(origin_string
))
952 const GURL origin
= storage::GetOriginFromIdentifier(origin_string
);
954 // Prepopulate the directory database(s) if and only if this instance
955 // has primary origin and the directory database is already there.
956 for (size_t i
= 0; i
< type_strings_to_prepopulate
.size(); ++i
) {
957 const std::string type_string
= type_strings_to_prepopulate
[i
];
958 // Only handles known types.
959 if (!ContainsKey(known_type_strings_
, type_string
))
961 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
962 base::FilePath path
= GetDirectoryForOriginAndType(
963 origin
, type_string
, false, &error
);
964 if (error
!= base::File::FILE_OK
)
966 scoped_ptr
<SandboxDirectoryDatabase
> db(
967 new SandboxDirectoryDatabase(path
, env_override_
));
968 if (db
->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION
)) {
969 directories_
[GetDirectoryDatabaseKey(origin
, type_string
)] = db
.release();
971 // Don't populate more than one database, as it may rather hurt
978 base::FilePath
ObfuscatedFileUtil::GetDirectoryForURL(
979 const FileSystemURL
& url
,
981 base::File::Error
* error_code
) {
982 return GetDirectoryForOriginAndType(
983 url
.origin(), CallGetTypeStringForURL(url
), create
, error_code
);
986 std::string
ObfuscatedFileUtil::CallGetTypeStringForURL(
987 const FileSystemURL
& url
) {
988 DCHECK(!get_type_string_for_url_
.is_null());
989 return get_type_string_for_url_
.Run(url
);
992 base::File::Error
ObfuscatedFileUtil::GetFileInfoInternal(
993 SandboxDirectoryDatabase
* db
,
994 FileSystemOperationContext
* context
,
995 const FileSystemURL
& url
,
997 FileInfo
* local_info
,
998 base::File::Info
* file_info
,
999 base::FilePath
* platform_file_path
) {
1003 DCHECK(platform_file_path
);
1005 if (!db
->GetFileInfo(file_id
, local_info
)) {
1007 return base::File::FILE_ERROR_FAILED
;
1010 if (local_info
->is_directory()) {
1011 file_info
->size
= 0;
1012 file_info
->is_directory
= true;
1013 file_info
->is_symbolic_link
= false;
1014 file_info
->last_modified
= local_info
->modification_time
;
1015 *platform_file_path
= base::FilePath();
1016 // We don't fill in ctime or atime.
1017 return base::File::FILE_OK
;
1019 if (local_info
->data_path
.empty())
1020 return base::File::FILE_ERROR_INVALID_OPERATION
;
1021 base::FilePath local_path
= DataPathToLocalPath(url
, local_info
->data_path
);
1022 base::File::Error error
= NativeFileUtil::GetFileInfo(
1023 local_path
, file_info
);
1024 // We should not follow symbolic links in sandboxed file system.
1025 if (base::IsLink(local_path
)) {
1026 LOG(WARNING
) << "Found a symbolic file.";
1027 error
= base::File::FILE_ERROR_NOT_FOUND
;
1029 if (error
== base::File::FILE_OK
) {
1030 *platform_file_path
= local_path
;
1031 } else if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1032 LOG(WARNING
) << "Lost a backing file.";
1033 InvalidateUsageCache(context
, url
.origin(), url
.type());
1034 if (!db
->RemoveFileInfo(file_id
))
1035 return base::File::FILE_ERROR_FAILED
;
1040 base::File
ObfuscatedFileUtil::CreateAndOpenFile(
1041 FileSystemOperationContext
* context
,
1042 const FileSystemURL
& dest_url
,
1043 FileInfo
* dest_file_info
, int file_flags
) {
1044 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1046 base::FilePath root
, dest_local_path
;
1047 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1049 if (error
!= base::File::FILE_OK
)
1050 return base::File(error
);
1052 if (base::PathExists(dest_local_path
)) {
1053 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1054 return base::File(base::File::FILE_ERROR_FAILED
);
1055 LOG(WARNING
) << "A stray file detected";
1056 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1059 base::File file
= NativeFileUtil::CreateOrOpen(dest_local_path
, file_flags
);
1060 if (!file
.IsValid())
1063 if (!file
.created()) {
1065 base::DeleteFile(dest_local_path
, false /* recursive */);
1066 return base::File(base::File::FILE_ERROR_FAILED
);
1069 error
= CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1070 if (error
!= base::File::FILE_OK
) {
1072 base::DeleteFile(dest_local_path
, false /* recursive */);
1073 return base::File(error
);
1079 base::File::Error
ObfuscatedFileUtil::CreateFile(
1080 FileSystemOperationContext
* context
,
1081 const base::FilePath
& src_file_path
,
1082 const FileSystemURL
& dest_url
,
1083 FileInfo
* dest_file_info
) {
1084 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1086 base::FilePath root
, dest_local_path
;
1087 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1089 if (error
!= base::File::FILE_OK
)
1092 bool created
= false;
1093 if (src_file_path
.empty()) {
1094 if (base::PathExists(dest_local_path
)) {
1095 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1096 return base::File::FILE_ERROR_FAILED
;
1097 LOG(WARNING
) << "A stray file detected";
1098 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1101 error
= NativeFileUtil::EnsureFileExists(dest_local_path
, &created
);
1103 error
= NativeFileUtil::CopyOrMoveFile(
1106 FileSystemOperation::OPTION_NONE
,
1107 storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
1111 if (error
!= base::File::FILE_OK
)
1114 return base::File::FILE_ERROR_FAILED
;
1116 return CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1119 base::File::Error
ObfuscatedFileUtil::CommitCreateFile(
1120 const base::FilePath
& root
,
1121 const base::FilePath
& local_path
,
1122 SandboxDirectoryDatabase
* db
,
1123 FileInfo
* dest_file_info
) {
1124 // This removes the root, including the trailing slash, leaving a relative
1126 dest_file_info
->data_path
= base::FilePath(
1127 local_path
.value().substr(root
.value().length() + 1));
1130 base::File::Error error
= db
->AddFileInfo(*dest_file_info
, &file_id
);
1131 if (error
!= base::File::FILE_OK
)
1134 TouchDirectory(db
, dest_file_info
->parent_id
);
1135 return base::File::FILE_OK
;
1138 base::FilePath
ObfuscatedFileUtil::DataPathToLocalPath(
1139 const FileSystemURL
& url
, const base::FilePath
& data_path
) {
1140 base::File::Error error
= base::File::FILE_OK
;
1141 base::FilePath root
= GetDirectoryForURL(url
, false, &error
);
1142 if (error
!= base::File::FILE_OK
)
1143 return base::FilePath();
1144 return root
.Append(data_path
);
1147 std::string
ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1148 const GURL
& origin
, const std::string
& type_string
) {
1149 // For isolated origin we just use a type string as a key.
1150 return storage::GetIdentifierFromOrigin(origin
) +
1151 kDirectoryDatabaseKeySeparator
+ type_string
;
1154 // TODO(ericu): How to do the whole validation-without-creation thing?
1155 // We may not have quota even to create the database.
1156 // Ah, in that case don't even get here?
1157 // Still doesn't answer the quota issue, though.
1158 SandboxDirectoryDatabase
* ObfuscatedFileUtil::GetDirectoryDatabase(
1159 const FileSystemURL
& url
, bool create
) {
1160 std::string key
= GetDirectoryDatabaseKey(
1161 url
.origin(), CallGetTypeStringForURL(url
));
1165 DirectoryMap::iterator iter
= directories_
.find(key
);
1166 if (iter
!= directories_
.end()) {
1168 return iter
->second
;
1171 base::File::Error error
= base::File::FILE_OK
;
1172 base::FilePath path
= GetDirectoryForURL(url
, create
, &error
);
1173 if (error
!= base::File::FILE_OK
) {
1174 LOG(WARNING
) << "Failed to get origin+type directory: "
1175 << url
.DebugString() << " error:" << error
;
1179 SandboxDirectoryDatabase
* database
=
1180 new SandboxDirectoryDatabase(path
, env_override_
);
1181 directories_
[key
] = database
;
1185 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOrigin(
1186 const GURL
& origin
, bool create
, base::File::Error
* error_code
) {
1187 if (!InitOriginDatabase(origin
, create
)) {
1189 *error_code
= create
?
1190 base::File::FILE_ERROR_FAILED
:
1191 base::File::FILE_ERROR_NOT_FOUND
;
1193 return base::FilePath();
1195 base::FilePath directory_name
;
1196 std::string id
= storage::GetIdentifierFromOrigin(origin
);
1198 bool exists_in_db
= origin_database_
->HasOriginPath(id
);
1199 if (!exists_in_db
&& !create
) {
1201 *error_code
= base::File::FILE_ERROR_NOT_FOUND
;
1202 return base::FilePath();
1204 if (!origin_database_
->GetPathForOrigin(id
, &directory_name
)) {
1206 *error_code
= base::File::FILE_ERROR_FAILED
;
1207 return base::FilePath();
1210 base::FilePath path
= file_system_directory_
.Append(directory_name
);
1211 bool exists_in_fs
= base::DirectoryExists(path
);
1212 if (!exists_in_db
&& exists_in_fs
) {
1213 if (!base::DeleteFile(path
, true)) {
1215 *error_code
= base::File::FILE_ERROR_FAILED
;
1216 return base::FilePath();
1218 exists_in_fs
= false;
1221 if (!exists_in_fs
) {
1222 if (!create
|| !base::CreateDirectory(path
)) {
1224 *error_code
= create
?
1225 base::File::FILE_ERROR_FAILED
:
1226 base::File::FILE_ERROR_NOT_FOUND
;
1227 return base::FilePath();
1232 *error_code
= base::File::FILE_OK
;
1237 void ObfuscatedFileUtil::InvalidateUsageCache(
1238 FileSystemOperationContext
* context
,
1240 FileSystemType type
) {
1241 if (sandbox_delegate_
)
1242 sandbox_delegate_
->InvalidateUsageCache(origin
, type
);
1245 void ObfuscatedFileUtil::MarkUsed() {
1247 timer_
.reset(new TimedTaskHelper(file_task_runner_
.get()));
1249 if (timer_
->IsRunning()) {
1252 timer_
->Start(FROM_HERE
,
1253 base::TimeDelta::FromSeconds(db_flush_delay_seconds_
),
1254 base::Bind(&ObfuscatedFileUtil::DropDatabases
,
1255 base::Unretained(this)));
1259 void ObfuscatedFileUtil::DropDatabases() {
1260 origin_database_
.reset();
1261 STLDeleteContainerPairSecondPointers(
1262 directories_
.begin(), directories_
.end());
1263 directories_
.clear();
1267 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL
& origin_hint
,
1269 if (origin_database_
)
1272 if (!create
&& !base::DirectoryExists(file_system_directory_
))
1274 if (!base::CreateDirectory(file_system_directory_
)) {
1275 LOG(WARNING
) << "Failed to create FileSystem directory: " <<
1276 file_system_directory_
.value();
1280 SandboxPrioritizedOriginDatabase
* prioritized_origin_database
=
1281 new SandboxPrioritizedOriginDatabase(file_system_directory_
,
1283 origin_database_
.reset(prioritized_origin_database
);
1285 if (origin_hint
.is_empty() || !HasIsolatedStorage(origin_hint
))
1288 const std::string isolated_origin_string
=
1289 storage::GetIdentifierFromOrigin(origin_hint
);
1291 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1292 base::FilePath isolated_origin_dir
= file_system_directory_
.Append(
1293 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory
);
1294 if (base::DirectoryExists(isolated_origin_dir
) &&
1295 prioritized_origin_database
->GetSandboxOriginDatabase()) {
1296 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1297 isolated_origin_string
,
1298 file_system_directory_
,
1299 prioritized_origin_database
->GetSandboxOriginDatabase());
1302 prioritized_origin_database
->InitializePrimaryOrigin(
1303 isolated_origin_string
);
1308 base::File::Error
ObfuscatedFileUtil::GenerateNewLocalPath(
1309 SandboxDirectoryDatabase
* db
,
1310 FileSystemOperationContext
* context
,
1311 const FileSystemURL
& url
,
1312 base::FilePath
* root
,
1313 base::FilePath
* local_path
) {
1316 if (!db
|| !db
->GetNextInteger(&number
))
1317 return base::File::FILE_ERROR_FAILED
;
1319 base::File::Error error
= base::File::FILE_OK
;
1320 *root
= GetDirectoryForURL(url
, false, &error
);
1321 if (error
!= base::File::FILE_OK
)
1324 // We use the third- and fourth-to-last digits as the directory.
1325 int64 directory_number
= number
% 10000 / 100;
1326 base::FilePath new_local_path
= root
->AppendASCII(
1327 base::StringPrintf("%02" PRId64
, directory_number
));
1329 error
= NativeFileUtil::CreateDirectory(
1330 new_local_path
, false /* exclusive */, false /* recursive */);
1331 if (error
!= base::File::FILE_OK
)
1335 new_local_path
.AppendASCII(base::StringPrintf("%08" PRId64
, number
));
1336 return base::File::FILE_OK
;
1339 base::File
ObfuscatedFileUtil::CreateOrOpenInternal(
1340 FileSystemOperationContext
* context
,
1341 const FileSystemURL
& url
, int file_flags
) {
1342 DCHECK(!(file_flags
& (base::File::FLAG_DELETE_ON_CLOSE
|
1343 base::File::FLAG_HIDDEN
| base::File::FLAG_EXCLUSIVE_READ
|
1344 base::File::FLAG_EXCLUSIVE_WRITE
)));
1345 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
1347 return base::File(base::File::FILE_ERROR_FAILED
);
1349 if (!db
->GetFileWithPath(url
.path(), &file_id
)) {
1350 // The file doesn't exist.
1351 if (!(file_flags
& (base::File::FLAG_CREATE
|
1352 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_OPEN_ALWAYS
))) {
1353 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1356 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
1357 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1359 InitFileInfo(&file_info
, parent_id
,
1360 VirtualPath::BaseName(url
.path()).value());
1362 int64 growth
= UsageForPath(file_info
.name
.size());
1363 if (!AllocateQuota(context
, growth
))
1364 return base::File(base::File::FILE_ERROR_NO_SPACE
);
1365 base::File file
= CreateAndOpenFile(context
, url
, &file_info
, file_flags
);
1366 if (file
.IsValid()) {
1367 UpdateUsage(context
, url
, growth
);
1368 context
->change_observers()->Notify(
1369 &FileChangeObserver::OnCreateFile
, base::MakeTuple(url
));
1374 if (file_flags
& base::File::FLAG_CREATE
)
1375 return base::File(base::File::FILE_ERROR_EXISTS
);
1377 base::File::Info platform_file_info
;
1378 base::FilePath local_path
;
1380 base::File::Error error
= GetFileInfoInternal(
1381 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
1382 if (error
!= base::File::FILE_OK
)
1383 return base::File(error
);
1384 if (file_info
.is_directory())
1385 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
1388 if (file_flags
& (base::File::FLAG_CREATE_ALWAYS
|
1389 base::File::FLAG_OPEN_TRUNCATED
)) {
1390 // The file exists and we're truncating.
1391 delta
= -platform_file_info
.size
;
1392 AllocateQuota(context
, delta
);
1395 base::File file
= NativeFileUtil::CreateOrOpen(local_path
, file_flags
);
1396 if (!file
.IsValid()) {
1397 error
= file
.error_details();
1398 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1399 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1400 // TODO(tzik): Delete database entry after ensuring the file lost.
1401 InvalidateUsageCache(context
, url
.origin(), url
.type());
1402 LOG(WARNING
) << "Lost a backing file.";
1403 return base::File(base::File::FILE_ERROR_FAILED
);
1408 // If truncating we need to update the usage.
1410 UpdateUsage(context
, url
, delta
);
1411 context
->change_observers()->Notify(
1412 &FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
1417 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL
& origin
) {
1418 return special_storage_policy_
.get() &&
1419 special_storage_policy_
->HasIsolatedStorage(origin
);
1422 } // namespace storage