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 (!base::StartsWith(iter
->first
, key_prefix
,
905 base::CompareCase::SENSITIVE
))
907 DCHECK(type_string
.empty() || iter
->first
== key_prefix
);
908 scoped_ptr
<SandboxDirectoryDatabase
> database(iter
->second
);
909 directories_
.erase(iter
++);
913 ObfuscatedFileUtil::AbstractOriginEnumerator
*
914 ObfuscatedFileUtil::CreateOriginEnumerator() {
915 std::vector
<SandboxOriginDatabase::OriginRecord
> origins
;
917 InitOriginDatabase(GURL(), false);
918 return new ObfuscatedOriginEnumerator(
919 origin_database_
.get(), file_system_directory_
);
922 void ObfuscatedFileUtil::DestroyDirectoryDatabase(
924 const std::string
& type_string
) {
925 // If |type_string| is empty, delete all filesystem types under |origin|.
926 const std::string key_prefix
= GetDirectoryDatabaseKey(origin
, type_string
);
927 for (DirectoryMap::iterator iter
= directories_
.lower_bound(key_prefix
);
928 iter
!= directories_
.end();) {
929 if (!base::StartsWith(iter
->first
, key_prefix
,
930 base::CompareCase::SENSITIVE
))
932 DCHECK(type_string
.empty() || iter
->first
== key_prefix
);
933 scoped_ptr
<SandboxDirectoryDatabase
> database(iter
->second
);
934 directories_
.erase(iter
++);
936 // Continue to destroy databases even if it failed because it doesn't affect
938 database
->DestroyDatabase();
943 int64
ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath
& path
) {
944 return UsageForPath(VirtualPath::BaseName(path
).value().size());
947 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
948 const std::vector
<std::string
>& type_strings_to_prepopulate
) {
949 SandboxPrioritizedOriginDatabase
database(file_system_directory_
,
951 std::string origin_string
= database
.GetPrimaryOrigin();
952 if (origin_string
.empty() || !database
.HasOriginPath(origin_string
))
954 const GURL origin
= storage::GetOriginFromIdentifier(origin_string
);
956 // Prepopulate the directory database(s) if and only if this instance
957 // has primary origin and the directory database is already there.
958 for (size_t i
= 0; i
< type_strings_to_prepopulate
.size(); ++i
) {
959 const std::string type_string
= type_strings_to_prepopulate
[i
];
960 // Only handles known types.
961 if (!ContainsKey(known_type_strings_
, type_string
))
963 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
964 base::FilePath path
= GetDirectoryForOriginAndType(
965 origin
, type_string
, false, &error
);
966 if (error
!= base::File::FILE_OK
)
968 scoped_ptr
<SandboxDirectoryDatabase
> db(
969 new SandboxDirectoryDatabase(path
, env_override_
));
970 if (db
->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION
)) {
971 directories_
[GetDirectoryDatabaseKey(origin
, type_string
)] = db
.release();
973 // Don't populate more than one database, as it may rather hurt
980 base::FilePath
ObfuscatedFileUtil::GetDirectoryForURL(
981 const FileSystemURL
& url
,
983 base::File::Error
* error_code
) {
984 return GetDirectoryForOriginAndType(
985 url
.origin(), CallGetTypeStringForURL(url
), create
, error_code
);
988 std::string
ObfuscatedFileUtil::CallGetTypeStringForURL(
989 const FileSystemURL
& url
) {
990 DCHECK(!get_type_string_for_url_
.is_null());
991 return get_type_string_for_url_
.Run(url
);
994 base::File::Error
ObfuscatedFileUtil::GetFileInfoInternal(
995 SandboxDirectoryDatabase
* db
,
996 FileSystemOperationContext
* context
,
997 const FileSystemURL
& url
,
999 FileInfo
* local_info
,
1000 base::File::Info
* file_info
,
1001 base::FilePath
* platform_file_path
) {
1005 DCHECK(platform_file_path
);
1007 if (!db
->GetFileInfo(file_id
, local_info
)) {
1009 return base::File::FILE_ERROR_FAILED
;
1012 if (local_info
->is_directory()) {
1013 file_info
->size
= 0;
1014 file_info
->is_directory
= true;
1015 file_info
->is_symbolic_link
= false;
1016 file_info
->last_modified
= local_info
->modification_time
;
1017 *platform_file_path
= base::FilePath();
1018 // We don't fill in ctime or atime.
1019 return base::File::FILE_OK
;
1021 if (local_info
->data_path
.empty())
1022 return base::File::FILE_ERROR_INVALID_OPERATION
;
1023 base::FilePath local_path
= DataPathToLocalPath(url
, local_info
->data_path
);
1024 base::File::Error error
= NativeFileUtil::GetFileInfo(
1025 local_path
, file_info
);
1026 // We should not follow symbolic links in sandboxed file system.
1027 if (base::IsLink(local_path
)) {
1028 LOG(WARNING
) << "Found a symbolic file.";
1029 error
= base::File::FILE_ERROR_NOT_FOUND
;
1031 if (error
== base::File::FILE_OK
) {
1032 *platform_file_path
= local_path
;
1033 } else if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1034 LOG(WARNING
) << "Lost a backing file.";
1035 InvalidateUsageCache(context
, url
.origin(), url
.type());
1036 if (!db
->RemoveFileInfo(file_id
))
1037 return base::File::FILE_ERROR_FAILED
;
1042 base::File
ObfuscatedFileUtil::CreateAndOpenFile(
1043 FileSystemOperationContext
* context
,
1044 const FileSystemURL
& dest_url
,
1045 FileInfo
* dest_file_info
, int file_flags
) {
1046 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1048 base::FilePath root
, dest_local_path
;
1049 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1051 if (error
!= base::File::FILE_OK
)
1052 return base::File(error
);
1054 if (base::PathExists(dest_local_path
)) {
1055 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1056 return base::File(base::File::FILE_ERROR_FAILED
);
1057 LOG(WARNING
) << "A stray file detected";
1058 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1061 base::File file
= NativeFileUtil::CreateOrOpen(dest_local_path
, file_flags
);
1062 if (!file
.IsValid())
1065 if (!file
.created()) {
1067 base::DeleteFile(dest_local_path
, false /* recursive */);
1068 return base::File(base::File::FILE_ERROR_FAILED
);
1071 error
= CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1072 if (error
!= base::File::FILE_OK
) {
1074 base::DeleteFile(dest_local_path
, false /* recursive */);
1075 return base::File(error
);
1081 base::File::Error
ObfuscatedFileUtil::CreateFile(
1082 FileSystemOperationContext
* context
,
1083 const base::FilePath
& src_file_path
,
1084 const FileSystemURL
& dest_url
,
1085 FileInfo
* dest_file_info
) {
1086 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1088 base::FilePath root
, dest_local_path
;
1089 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1091 if (error
!= base::File::FILE_OK
)
1094 bool created
= false;
1095 if (src_file_path
.empty()) {
1096 if (base::PathExists(dest_local_path
)) {
1097 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1098 return base::File::FILE_ERROR_FAILED
;
1099 LOG(WARNING
) << "A stray file detected";
1100 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1103 error
= NativeFileUtil::EnsureFileExists(dest_local_path
, &created
);
1105 error
= NativeFileUtil::CopyOrMoveFile(
1108 FileSystemOperation::OPTION_NONE
,
1109 storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
1113 if (error
!= base::File::FILE_OK
)
1116 return base::File::FILE_ERROR_FAILED
;
1118 return CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1121 base::File::Error
ObfuscatedFileUtil::CommitCreateFile(
1122 const base::FilePath
& root
,
1123 const base::FilePath
& local_path
,
1124 SandboxDirectoryDatabase
* db
,
1125 FileInfo
* dest_file_info
) {
1126 // This removes the root, including the trailing slash, leaving a relative
1128 dest_file_info
->data_path
= base::FilePath(
1129 local_path
.value().substr(root
.value().length() + 1));
1132 base::File::Error error
= db
->AddFileInfo(*dest_file_info
, &file_id
);
1133 if (error
!= base::File::FILE_OK
)
1136 TouchDirectory(db
, dest_file_info
->parent_id
);
1137 return base::File::FILE_OK
;
1140 base::FilePath
ObfuscatedFileUtil::DataPathToLocalPath(
1141 const FileSystemURL
& url
, const base::FilePath
& data_path
) {
1142 base::File::Error error
= base::File::FILE_OK
;
1143 base::FilePath root
= GetDirectoryForURL(url
, false, &error
);
1144 if (error
!= base::File::FILE_OK
)
1145 return base::FilePath();
1146 return root
.Append(data_path
);
1149 std::string
ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1150 const GURL
& origin
, const std::string
& type_string
) {
1151 // For isolated origin we just use a type string as a key.
1152 return storage::GetIdentifierFromOrigin(origin
) +
1153 kDirectoryDatabaseKeySeparator
+ type_string
;
1156 // TODO(ericu): How to do the whole validation-without-creation thing?
1157 // We may not have quota even to create the database.
1158 // Ah, in that case don't even get here?
1159 // Still doesn't answer the quota issue, though.
1160 SandboxDirectoryDatabase
* ObfuscatedFileUtil::GetDirectoryDatabase(
1161 const FileSystemURL
& url
, bool create
) {
1162 std::string key
= GetDirectoryDatabaseKey(
1163 url
.origin(), CallGetTypeStringForURL(url
));
1167 DirectoryMap::iterator iter
= directories_
.find(key
);
1168 if (iter
!= directories_
.end()) {
1170 return iter
->second
;
1173 base::File::Error error
= base::File::FILE_OK
;
1174 base::FilePath path
= GetDirectoryForURL(url
, create
, &error
);
1175 if (error
!= base::File::FILE_OK
) {
1176 LOG(WARNING
) << "Failed to get origin+type directory: "
1177 << url
.DebugString() << " error:" << error
;
1181 SandboxDirectoryDatabase
* database
=
1182 new SandboxDirectoryDatabase(path
, env_override_
);
1183 directories_
[key
] = database
;
1187 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOrigin(
1188 const GURL
& origin
, bool create
, base::File::Error
* error_code
) {
1189 if (!InitOriginDatabase(origin
, create
)) {
1191 *error_code
= create
?
1192 base::File::FILE_ERROR_FAILED
:
1193 base::File::FILE_ERROR_NOT_FOUND
;
1195 return base::FilePath();
1197 base::FilePath directory_name
;
1198 std::string id
= storage::GetIdentifierFromOrigin(origin
);
1200 bool exists_in_db
= origin_database_
->HasOriginPath(id
);
1201 if (!exists_in_db
&& !create
) {
1203 *error_code
= base::File::FILE_ERROR_NOT_FOUND
;
1204 return base::FilePath();
1206 if (!origin_database_
->GetPathForOrigin(id
, &directory_name
)) {
1208 *error_code
= base::File::FILE_ERROR_FAILED
;
1209 return base::FilePath();
1212 base::FilePath path
= file_system_directory_
.Append(directory_name
);
1213 bool exists_in_fs
= base::DirectoryExists(path
);
1214 if (!exists_in_db
&& exists_in_fs
) {
1215 if (!base::DeleteFile(path
, true)) {
1217 *error_code
= base::File::FILE_ERROR_FAILED
;
1218 return base::FilePath();
1220 exists_in_fs
= false;
1223 if (!exists_in_fs
) {
1224 if (!create
|| !base::CreateDirectory(path
)) {
1226 *error_code
= create
?
1227 base::File::FILE_ERROR_FAILED
:
1228 base::File::FILE_ERROR_NOT_FOUND
;
1229 return base::FilePath();
1234 *error_code
= base::File::FILE_OK
;
1239 void ObfuscatedFileUtil::InvalidateUsageCache(
1240 FileSystemOperationContext
* context
,
1242 FileSystemType type
) {
1243 if (sandbox_delegate_
)
1244 sandbox_delegate_
->InvalidateUsageCache(origin
, type
);
1247 void ObfuscatedFileUtil::MarkUsed() {
1249 timer_
.reset(new TimedTaskHelper(file_task_runner_
.get()));
1251 if (timer_
->IsRunning()) {
1254 timer_
->Start(FROM_HERE
,
1255 base::TimeDelta::FromSeconds(db_flush_delay_seconds_
),
1256 base::Bind(&ObfuscatedFileUtil::DropDatabases
,
1257 base::Unretained(this)));
1261 void ObfuscatedFileUtil::DropDatabases() {
1262 origin_database_
.reset();
1263 STLDeleteContainerPairSecondPointers(
1264 directories_
.begin(), directories_
.end());
1265 directories_
.clear();
1269 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL
& origin_hint
,
1271 if (origin_database_
)
1274 if (!create
&& !base::DirectoryExists(file_system_directory_
))
1276 if (!base::CreateDirectory(file_system_directory_
)) {
1277 LOG(WARNING
) << "Failed to create FileSystem directory: " <<
1278 file_system_directory_
.value();
1282 SandboxPrioritizedOriginDatabase
* prioritized_origin_database
=
1283 new SandboxPrioritizedOriginDatabase(file_system_directory_
,
1285 origin_database_
.reset(prioritized_origin_database
);
1287 if (origin_hint
.is_empty() || !HasIsolatedStorage(origin_hint
))
1290 const std::string isolated_origin_string
=
1291 storage::GetIdentifierFromOrigin(origin_hint
);
1293 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1294 base::FilePath isolated_origin_dir
= file_system_directory_
.Append(
1295 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory
);
1296 if (base::DirectoryExists(isolated_origin_dir
) &&
1297 prioritized_origin_database
->GetSandboxOriginDatabase()) {
1298 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1299 isolated_origin_string
,
1300 file_system_directory_
,
1301 prioritized_origin_database
->GetSandboxOriginDatabase());
1304 prioritized_origin_database
->InitializePrimaryOrigin(
1305 isolated_origin_string
);
1310 base::File::Error
ObfuscatedFileUtil::GenerateNewLocalPath(
1311 SandboxDirectoryDatabase
* db
,
1312 FileSystemOperationContext
* context
,
1313 const FileSystemURL
& url
,
1314 base::FilePath
* root
,
1315 base::FilePath
* local_path
) {
1318 if (!db
|| !db
->GetNextInteger(&number
))
1319 return base::File::FILE_ERROR_FAILED
;
1321 base::File::Error error
= base::File::FILE_OK
;
1322 *root
= GetDirectoryForURL(url
, false, &error
);
1323 if (error
!= base::File::FILE_OK
)
1326 // We use the third- and fourth-to-last digits as the directory.
1327 int64 directory_number
= number
% 10000 / 100;
1328 base::FilePath new_local_path
= root
->AppendASCII(
1329 base::StringPrintf("%02" PRId64
, directory_number
));
1331 error
= NativeFileUtil::CreateDirectory(
1332 new_local_path
, false /* exclusive */, false /* recursive */);
1333 if (error
!= base::File::FILE_OK
)
1337 new_local_path
.AppendASCII(base::StringPrintf("%08" PRId64
, number
));
1338 return base::File::FILE_OK
;
1341 base::File
ObfuscatedFileUtil::CreateOrOpenInternal(
1342 FileSystemOperationContext
* context
,
1343 const FileSystemURL
& url
, int file_flags
) {
1344 DCHECK(!(file_flags
& (base::File::FLAG_DELETE_ON_CLOSE
|
1345 base::File::FLAG_HIDDEN
| base::File::FLAG_EXCLUSIVE_READ
|
1346 base::File::FLAG_EXCLUSIVE_WRITE
)));
1347 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
1349 return base::File(base::File::FILE_ERROR_FAILED
);
1351 if (!db
->GetFileWithPath(url
.path(), &file_id
)) {
1352 // The file doesn't exist.
1353 if (!(file_flags
& (base::File::FLAG_CREATE
|
1354 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_OPEN_ALWAYS
))) {
1355 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1358 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
1359 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1361 InitFileInfo(&file_info
, parent_id
,
1362 VirtualPath::BaseName(url
.path()).value());
1364 int64 growth
= UsageForPath(file_info
.name
.size());
1365 if (!AllocateQuota(context
, growth
))
1366 return base::File(base::File::FILE_ERROR_NO_SPACE
);
1367 base::File file
= CreateAndOpenFile(context
, url
, &file_info
, file_flags
);
1368 if (file
.IsValid()) {
1369 UpdateUsage(context
, url
, growth
);
1370 context
->change_observers()->Notify(
1371 &FileChangeObserver::OnCreateFile
, base::MakeTuple(url
));
1376 if (file_flags
& base::File::FLAG_CREATE
)
1377 return base::File(base::File::FILE_ERROR_EXISTS
);
1379 base::File::Info platform_file_info
;
1380 base::FilePath local_path
;
1382 base::File::Error error
= GetFileInfoInternal(
1383 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
1384 if (error
!= base::File::FILE_OK
)
1385 return base::File(error
);
1386 if (file_info
.is_directory())
1387 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
1390 if (file_flags
& (base::File::FLAG_CREATE_ALWAYS
|
1391 base::File::FLAG_OPEN_TRUNCATED
)) {
1392 // The file exists and we're truncating.
1393 delta
= -platform_file_info
.size
;
1394 AllocateQuota(context
, delta
);
1397 base::File file
= NativeFileUtil::CreateOrOpen(local_path
, file_flags
);
1398 if (!file
.IsValid()) {
1399 error
= file
.error_details();
1400 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1401 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1402 // TODO(tzik): Delete database entry after ensuring the file lost.
1403 InvalidateUsageCache(context
, url
.origin(), url
.type());
1404 LOG(WARNING
) << "Lost a backing file.";
1405 return base::File(base::File::FILE_ERROR_FAILED
);
1410 // If truncating we need to update the usage.
1412 UpdateUsage(context
, url
, delta
);
1413 context
->change_observers()->Notify(
1414 &FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
1419 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL
& origin
) {
1420 return special_storage_policy_
.get() &&
1421 special_storage_policy_
->HasIsolatedStorage(origin
);
1424 } // namespace storage