1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webkit/browser/fileapi/obfuscated_file_util.h"
11 #include "base/file_util.h"
12 #include "base/format_macros.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
23 #include "webkit/browser/fileapi/file_observers.h"
24 #include "webkit/browser/fileapi/file_system_context.h"
25 #include "webkit/browser/fileapi/file_system_operation_context.h"
26 #include "webkit/browser/fileapi/file_system_url.h"
27 #include "webkit/browser/fileapi/native_file_util.h"
28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
29 #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
30 #include "webkit/browser/fileapi/sandbox_origin_database.h"
31 #include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h"
32 #include "webkit/browser/fileapi/timed_task_helper.h"
33 #include "webkit/browser/quota/quota_manager.h"
34 #include "webkit/common/database/database_identifier.h"
35 #include "webkit/common/fileapi/file_system_util.h"
37 // Example of various paths:
38 // void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
39 // base::FilePath virtual_path = url.path();
40 // base::FilePath local_path = GetLocalFilePath(url);
42 // NativeFileUtil::DoSomething(local_path);
43 // file_util::DoAnother(local_path);
50 typedef SandboxDirectoryDatabase::FileId FileId
;
51 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
54 SandboxDirectoryDatabase::FileInfo
* file_info
,
55 SandboxDirectoryDatabase::FileId parent_id
,
56 const base::FilePath::StringType
& file_name
) {
58 file_info
->parent_id
= parent_id
;
59 file_info
->name
= file_name
;
62 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of
63 // path storage under Linux. It's not clear if that will differ on Windows, on
64 // which base::FilePath uses wide chars [since they're converted to UTF-8 for
65 // storage anyway], but as long as the cost is high enough that one can't cheat
66 // on quota by storing data in paths, it doesn't need to be all that accurate.
67 const int64 kPathCreationQuotaCost
= 146; // Bytes per inode, basically.
68 const int64 kPathByteQuotaCost
= 2; // Bytes per byte of path length in UTF-8.
70 int64
UsageForPath(size_t length
) {
71 return kPathCreationQuotaCost
+
72 static_cast<int64
>(length
) * kPathByteQuotaCost
;
75 bool AllocateQuota(FileSystemOperationContext
* context
, int64 growth
) {
76 if (context
->allowed_bytes_growth() == quota::QuotaManager::kNoLimit
)
79 int64 new_quota
= context
->allowed_bytes_growth() - growth
;
80 if (growth
> 0 && new_quota
< 0)
82 context
->set_allowed_bytes_growth(new_quota
);
87 FileSystemOperationContext
* context
,
88 const FileSystemURL
& url
,
90 context
->update_observers()->Notify(
91 &FileUpdateObserver::OnUpdate
, MakeTuple(url
, growth
));
94 void TouchDirectory(SandboxDirectoryDatabase
* db
, FileId dir_id
) {
96 if (!db
->UpdateModificationTime(dir_id
, base::Time::Now()))
100 enum IsolatedOriginStatus
{
101 kIsolatedOriginMatch
,
102 kIsolatedOriginDontMatch
,
103 kIsolatedOriginStatusMax
,
108 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 virtual ~ObfuscatedFileEnumerator() {}
135 virtual 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 virtual int64
Size() OVERRIDE
{
162 return current_platform_file_info_
.size
;
165 virtual base::Time
LastModifiedTime() OVERRIDE
{
166 return current_platform_file_info_
.last_modified
;
169 virtual bool IsDirectory() OVERRIDE
{
170 return current_platform_file_info_
.is_directory
;
174 typedef SandboxDirectoryDatabase::FileId FileId
;
175 typedef SandboxDirectoryDatabase::FileInfo FileInfo
;
179 base::FilePath virtual_path
;
182 void ProcessRecurseQueue() {
183 while (display_stack_
.empty() && !recurse_queue_
.empty()) {
184 FileRecord entry
= recurse_queue_
.front();
185 recurse_queue_
.pop();
186 if (!db_
->ListChildren(entry
.file_id
, &display_stack_
)) {
187 display_stack_
.clear();
190 current_parent_virtual_path_
= entry
.virtual_path
;
194 SandboxDirectoryDatabase
* db_
;
195 FileSystemOperationContext
* context_
;
196 ObfuscatedFileUtil
* obfuscated_file_util_
;
197 FileSystemURL root_url_
;
200 std::queue
<FileRecord
> recurse_queue_
;
201 std::vector
<FileId
> display_stack_
;
202 base::FilePath current_parent_virtual_path_
;
204 FileId current_file_id_
;
205 base::File::Info current_platform_file_info_
;
208 class ObfuscatedOriginEnumerator
209 : public ObfuscatedFileUtil::AbstractOriginEnumerator
{
211 typedef SandboxOriginDatabase::OriginRecord OriginRecord
;
212 ObfuscatedOriginEnumerator(
213 SandboxOriginDatabaseInterface
* origin_database
,
214 const base::FilePath
& base_file_path
)
215 : base_file_path_(base_file_path
) {
217 origin_database
->ListAllOrigins(&origins_
);
220 virtual ~ObfuscatedOriginEnumerator() {}
222 // Returns the next origin. Returns empty if there are no more origins.
223 virtual GURL
Next() OVERRIDE
{
225 if (!origins_
.empty()) {
226 record
= origins_
.back();
230 return webkit_database::GetOriginFromIdentifier(record
.origin
);
233 // Returns the current origin's information.
234 virtual bool HasTypeDirectory(const std::string
& type_string
) const OVERRIDE
{
235 if (current_
.path
.empty())
237 if (type_string
.empty()) {
241 base::FilePath path
=
242 base_file_path_
.Append(current_
.path
).AppendASCII(type_string
);
243 return base::DirectoryExists(path
);
247 std::vector
<OriginRecord
> origins_
;
248 OriginRecord current_
;
249 base::FilePath base_file_path_
;
252 ObfuscatedFileUtil::ObfuscatedFileUtil(
253 quota::SpecialStoragePolicy
* special_storage_policy
,
254 const base::FilePath
& file_system_directory
,
255 leveldb::Env
* env_override
,
256 base::SequencedTaskRunner
* file_task_runner
,
257 const GetTypeStringForURLCallback
& get_type_string_for_url
,
258 const std::set
<std::string
>& known_type_strings
,
259 SandboxFileSystemBackendDelegate
* sandbox_delegate
)
260 : special_storage_policy_(special_storage_policy
),
261 file_system_directory_(file_system_directory
),
262 env_override_(env_override
),
263 db_flush_delay_seconds_(10 * 60), // 10 mins.
264 file_task_runner_(file_task_runner
),
265 get_type_string_for_url_(get_type_string_for_url
),
266 known_type_strings_(known_type_strings
),
267 sandbox_delegate_(sandbox_delegate
) {
270 ObfuscatedFileUtil::~ObfuscatedFileUtil() {
274 base::File
ObfuscatedFileUtil::CreateOrOpen(
275 FileSystemOperationContext
* context
,
276 const FileSystemURL
& url
, int file_flags
) {
277 base::File file
= CreateOrOpenInternal(context
, url
, file_flags
);
278 if (file
.IsValid() && file_flags
& base::PLATFORM_FILE_WRITE
&&
279 context
->quota_limit_type() == quota::kQuotaLimitTypeUnlimited
&&
281 sandbox_delegate_
->StickyInvalidateUsageCache(url
.origin(), url
.type());
286 base::File::Error
ObfuscatedFileUtil::EnsureFileExists(
287 FileSystemOperationContext
* context
,
288 const FileSystemURL
& url
,
290 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
292 return base::File::FILE_ERROR_FAILED
;
295 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
297 if (!db
->GetFileInfo(file_id
, &file_info
)) {
299 return base::File::FILE_ERROR_FAILED
;
301 if (file_info
.is_directory())
302 return base::File::FILE_ERROR_NOT_A_FILE
;
305 return base::File::FILE_OK
;
308 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
309 return base::File::FILE_ERROR_NOT_FOUND
;
312 InitFileInfo(&file_info
, parent_id
,
313 VirtualPath::BaseName(url
.path()).value());
315 int64 growth
= UsageForPath(file_info
.name
.size());
316 if (!AllocateQuota(context
, growth
))
317 return base::File::FILE_ERROR_NO_SPACE
;
318 base::File::Error error
= CreateFile(context
, base::FilePath(), url
,
320 if (created
&& base::File::FILE_OK
== error
) {
322 UpdateUsage(context
, url
, growth
);
323 context
->change_observers()->Notify(
324 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
329 base::File::Error
ObfuscatedFileUtil::CreateDirectory(
330 FileSystemOperationContext
* context
,
331 const FileSystemURL
& url
,
334 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
336 return base::File::FILE_ERROR_FAILED
;
339 if (db
->GetFileWithPath(url
.path(), &file_id
)) {
342 return base::File::FILE_ERROR_EXISTS
;
343 if (!db
->GetFileInfo(file_id
, &file_info
)) {
345 return base::File::FILE_ERROR_FAILED
;
347 if (!file_info
.is_directory())
348 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
349 return base::File::FILE_OK
;
352 std::vector
<base::FilePath::StringType
> components
;
353 VirtualPath::GetComponents(url
.path(), &components
);
354 FileId parent_id
= 0;
356 for (index
= 0; index
< components
.size(); ++index
) {
357 base::FilePath::StringType name
= components
[index
];
358 if (name
== FILE_PATH_LITERAL("/"))
360 if (!db
->GetChildWithName(parent_id
, name
, &parent_id
))
363 if (!db
->IsDirectory(parent_id
))
364 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
365 if (!recursive
&& components
.size() - index
> 1)
366 return base::File::FILE_ERROR_NOT_FOUND
;
368 for (; index
< components
.size(); ++index
) {
370 file_info
.name
= components
[index
];
371 if (file_info
.name
== FILE_PATH_LITERAL("/"))
373 file_info
.modification_time
= base::Time::Now();
374 file_info
.parent_id
= parent_id
;
375 int64 growth
= UsageForPath(file_info
.name
.size());
376 if (!AllocateQuota(context
, growth
))
377 return base::File::FILE_ERROR_NO_SPACE
;
378 base::File::Error error
= db
->AddFileInfo(file_info
, &parent_id
);
379 if (error
!= base::File::FILE_OK
)
381 UpdateUsage(context
, url
, growth
);
382 context
->change_observers()->Notify(
383 &FileChangeObserver::OnCreateDirectory
, MakeTuple(url
));
386 TouchDirectory(db
, file_info
.parent_id
);
389 return base::File::FILE_OK
;
392 base::File::Error
ObfuscatedFileUtil::GetFileInfo(
393 FileSystemOperationContext
* context
,
394 const FileSystemURL
& url
,
395 base::File::Info
* file_info
,
396 base::FilePath
* platform_file_path
) {
397 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
399 return base::File::FILE_ERROR_NOT_FOUND
;
401 if (!db
->GetFileWithPath(url
.path(), &file_id
))
402 return base::File::FILE_ERROR_NOT_FOUND
;
404 return GetFileInfoInternal(db
, context
, url
,
405 file_id
, &local_info
,
406 file_info
, platform_file_path
);
409 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
410 ObfuscatedFileUtil::CreateFileEnumerator(
411 FileSystemOperationContext
* context
,
412 const FileSystemURL
& root_url
) {
413 return CreateFileEnumerator(context
, root_url
, false /* recursive */);
416 base::File::Error
ObfuscatedFileUtil::GetLocalFilePath(
417 FileSystemOperationContext
* context
,
418 const FileSystemURL
& url
,
419 base::FilePath
* local_path
) {
420 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
422 return base::File::FILE_ERROR_NOT_FOUND
;
424 if (!db
->GetFileWithPath(url
.path(), &file_id
))
425 return base::File::FILE_ERROR_NOT_FOUND
;
427 if (!db
->GetFileInfo(file_id
, &file_info
) || file_info
.is_directory()) {
429 // Directories have no local file path.
430 return base::File::FILE_ERROR_NOT_FOUND
;
432 *local_path
= DataPathToLocalPath(url
, file_info
.data_path
);
434 if (local_path
->empty())
435 return base::File::FILE_ERROR_NOT_FOUND
;
436 return base::File::FILE_OK
;
439 base::File::Error
ObfuscatedFileUtil::Touch(
440 FileSystemOperationContext
* context
,
441 const FileSystemURL
& url
,
442 const base::Time
& last_access_time
,
443 const base::Time
& last_modified_time
) {
444 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
446 return base::File::FILE_ERROR_NOT_FOUND
;
448 if (!db
->GetFileWithPath(url
.path(), &file_id
))
449 return base::File::FILE_ERROR_NOT_FOUND
;
452 if (!db
->GetFileInfo(file_id
, &file_info
)) {
454 return base::File::FILE_ERROR_FAILED
;
456 if (file_info
.is_directory()) {
457 if (!db
->UpdateModificationTime(file_id
, last_modified_time
))
458 return base::File::FILE_ERROR_FAILED
;
459 return base::File::FILE_OK
;
461 return NativeFileUtil::Touch(
462 DataPathToLocalPath(url
, file_info
.data_path
),
463 last_access_time
, last_modified_time
);
466 base::File::Error
ObfuscatedFileUtil::Truncate(
467 FileSystemOperationContext
* context
,
468 const FileSystemURL
& url
,
470 base::File::Info file_info
;
471 base::FilePath local_path
;
472 base::File::Error error
=
473 GetFileInfo(context
, url
, &file_info
, &local_path
);
474 if (error
!= base::File::FILE_OK
)
477 int64 growth
= length
- file_info
.size
;
478 if (!AllocateQuota(context
, growth
))
479 return base::File::FILE_ERROR_NO_SPACE
;
480 error
= NativeFileUtil::Truncate(local_path
, length
);
481 if (error
== base::File::FILE_OK
) {
482 UpdateUsage(context
, url
, growth
);
483 context
->change_observers()->Notify(
484 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
489 base::File::Error
ObfuscatedFileUtil::CopyOrMoveFile(
490 FileSystemOperationContext
* context
,
491 const FileSystemURL
& src_url
,
492 const FileSystemURL
& dest_url
,
493 CopyOrMoveOption option
,
495 // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
496 DCHECK(src_url
.origin() == dest_url
.origin());
497 DCHECK(src_url
.type() == dest_url
.type());
499 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(src_url
, true);
501 return base::File::FILE_ERROR_FAILED
;
504 if (!db
->GetFileWithPath(src_url
.path(), &src_file_id
))
505 return base::File::FILE_ERROR_NOT_FOUND
;
508 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
511 FileInfo src_file_info
;
512 base::File::Info src_platform_file_info
;
513 base::FilePath src_local_path
;
514 base::File::Error error
= GetFileInfoInternal(
515 db
, context
, src_url
, src_file_id
,
516 &src_file_info
, &src_platform_file_info
, &src_local_path
);
517 if (error
!= base::File::FILE_OK
)
519 if (src_file_info
.is_directory())
520 return base::File::FILE_ERROR_NOT_A_FILE
;
522 FileInfo dest_file_info
;
523 base::File::Info dest_platform_file_info
; // overwrite case only
524 base::FilePath dest_local_path
; // overwrite case only
526 base::File::Error error
= GetFileInfoInternal(
527 db
, context
, dest_url
, dest_file_id
,
528 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
529 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
530 overwrite
= false; // fallback to non-overwrite case
531 else if (error
!= base::File::FILE_OK
)
533 else if (dest_file_info
.is_directory())
534 return base::File::FILE_ERROR_INVALID_OPERATION
;
537 FileId dest_parent_id
;
538 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
540 return base::File::FILE_ERROR_NOT_FOUND
;
543 dest_file_info
= src_file_info
;
544 dest_file_info
.parent_id
= dest_parent_id
;
545 dest_file_info
.name
=
546 VirtualPath::BaseName(dest_url
.path()).value();
551 growth
+= src_platform_file_info
.size
;
553 growth
-= UsageForPath(src_file_info
.name
.size());
555 growth
-= dest_platform_file_info
.size
;
557 growth
+= UsageForPath(dest_file_info
.name
.size());
558 if (!AllocateQuota(context
, growth
))
559 return base::File::FILE_ERROR_NO_SPACE
;
562 * Copy-with-overwrite
563 * Just overwrite data file
564 * Copy-without-overwrite
566 * Create new metadata pointing to new backing file.
567 * Move-with-overwrite
569 * Remove source entry.
570 * Point target entry to source entry's backing file.
571 * Delete target entry's old backing file
572 * Move-without-overwrite
573 * Just update metadata
575 error
= base::File::FILE_ERROR_FAILED
;
578 error
= NativeFileUtil::CopyOrMoveFile(
582 fileapi::NativeFileUtil::CopyOrMoveModeForDestination(
583 dest_url
, true /* copy */));
584 } else { // non-overwrite
585 error
= CreateFile(context
, src_local_path
, dest_url
, &dest_file_info
);
589 if (db
->OverwritingMoveFile(src_file_id
, dest_file_id
)) {
590 if (base::File::FILE_OK
!=
591 NativeFileUtil::DeleteFile(dest_local_path
))
592 LOG(WARNING
) << "Leaked a backing file.";
593 error
= base::File::FILE_OK
;
595 error
= base::File::FILE_ERROR_FAILED
;
597 } else { // non-overwrite
598 if (db
->UpdateFileInfo(src_file_id
, dest_file_info
))
599 error
= base::File::FILE_OK
;
601 error
= base::File::FILE_ERROR_FAILED
;
605 if (error
!= base::File::FILE_OK
)
609 context
->change_observers()->Notify(
610 &FileChangeObserver::OnModifyFile
,
611 MakeTuple(dest_url
));
613 context
->change_observers()->Notify(
614 &FileChangeObserver::OnCreateFileFrom
,
615 MakeTuple(dest_url
, src_url
));
619 context
->change_observers()->Notify(
620 &FileChangeObserver::OnRemoveFile
, MakeTuple(src_url
));
621 TouchDirectory(db
, src_file_info
.parent_id
);
624 TouchDirectory(db
, dest_file_info
.parent_id
);
626 UpdateUsage(context
, dest_url
, growth
);
630 base::File::Error
ObfuscatedFileUtil::CopyInForeignFile(
631 FileSystemOperationContext
* context
,
632 const base::FilePath
& src_file_path
,
633 const FileSystemURL
& dest_url
) {
634 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
636 return base::File::FILE_ERROR_FAILED
;
638 base::File::Info src_platform_file_info
;
639 if (!base::GetFileInfo(src_file_path
, &src_platform_file_info
))
640 return base::File::FILE_ERROR_NOT_FOUND
;
643 bool overwrite
= db
->GetFileWithPath(dest_url
.path(),
646 FileInfo dest_file_info
;
647 base::File::Info dest_platform_file_info
; // overwrite case only
649 base::FilePath dest_local_path
;
650 base::File::Error error
= GetFileInfoInternal(
651 db
, context
, dest_url
, dest_file_id
,
652 &dest_file_info
, &dest_platform_file_info
, &dest_local_path
);
653 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
654 overwrite
= false; // fallback to non-overwrite case
655 else if (error
!= base::File::FILE_OK
)
657 else if (dest_file_info
.is_directory())
658 return base::File::FILE_ERROR_INVALID_OPERATION
;
661 FileId dest_parent_id
;
662 if (!db
->GetFileWithPath(VirtualPath::DirName(dest_url
.path()),
664 return base::File::FILE_ERROR_NOT_FOUND
;
666 if (!dest_file_info
.is_directory())
667 return base::File::FILE_ERROR_FAILED
;
668 InitFileInfo(&dest_file_info
, dest_parent_id
,
669 VirtualPath::BaseName(dest_url
.path()).value());
672 int64 growth
= src_platform_file_info
.size
;
674 growth
-= dest_platform_file_info
.size
;
676 growth
+= UsageForPath(dest_file_info
.name
.size());
677 if (!AllocateQuota(context
, growth
))
678 return base::File::FILE_ERROR_NO_SPACE
;
680 base::File::Error error
;
682 base::FilePath dest_local_path
=
683 DataPathToLocalPath(dest_url
, dest_file_info
.data_path
);
684 error
= NativeFileUtil::CopyOrMoveFile(
685 src_file_path
, dest_local_path
,
686 FileSystemOperation::OPTION_NONE
,
687 fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
690 error
= CreateFile(context
, src_file_path
, dest_url
, &dest_file_info
);
693 if (error
!= base::File::FILE_OK
)
697 context
->change_observers()->Notify(
698 &FileChangeObserver::OnModifyFile
, MakeTuple(dest_url
));
700 context
->change_observers()->Notify(
701 &FileChangeObserver::OnCreateFile
, MakeTuple(dest_url
));
704 UpdateUsage(context
, dest_url
, growth
);
705 TouchDirectory(db
, dest_file_info
.parent_id
);
706 return base::File::FILE_OK
;
709 base::File::Error
ObfuscatedFileUtil::DeleteFile(
710 FileSystemOperationContext
* context
,
711 const FileSystemURL
& url
) {
712 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
714 return base::File::FILE_ERROR_FAILED
;
716 if (!db
->GetFileWithPath(url
.path(), &file_id
))
717 return base::File::FILE_ERROR_NOT_FOUND
;
720 base::File::Info platform_file_info
;
721 base::FilePath local_path
;
722 base::File::Error error
= GetFileInfoInternal(
723 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
724 if (error
!= base::File::FILE_ERROR_NOT_FOUND
&&
725 error
!= base::File::FILE_OK
)
728 if (file_info
.is_directory())
729 return base::File::FILE_ERROR_NOT_A_FILE
;
731 int64 growth
= -UsageForPath(file_info
.name
.size()) - platform_file_info
.size
;
732 AllocateQuota(context
, growth
);
733 if (!db
->RemoveFileInfo(file_id
)) {
735 return base::File::FILE_ERROR_FAILED
;
737 UpdateUsage(context
, url
, growth
);
738 TouchDirectory(db
, file_info
.parent_id
);
740 context
->change_observers()->Notify(
741 &FileChangeObserver::OnRemoveFile
, MakeTuple(url
));
743 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
744 return base::File::FILE_OK
;
746 error
= NativeFileUtil::DeleteFile(local_path
);
747 if (base::File::FILE_OK
!= error
)
748 LOG(WARNING
) << "Leaked a backing file.";
749 return base::File::FILE_OK
;
752 base::File::Error
ObfuscatedFileUtil::DeleteDirectory(
753 FileSystemOperationContext
* context
,
754 const FileSystemURL
& url
) {
755 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
757 return base::File::FILE_ERROR_FAILED
;
760 if (!db
->GetFileWithPath(url
.path(), &file_id
))
761 return base::File::FILE_ERROR_NOT_FOUND
;
763 if (!db
->GetFileInfo(file_id
, &file_info
)) {
765 return base::File::FILE_ERROR_FAILED
;
767 if (!file_info
.is_directory())
768 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
769 if (!db
->RemoveFileInfo(file_id
))
770 return base::File::FILE_ERROR_NOT_EMPTY
;
771 int64 growth
= -UsageForPath(file_info
.name
.size());
772 AllocateQuota(context
, growth
);
773 UpdateUsage(context
, url
, growth
);
774 TouchDirectory(db
, file_info
.parent_id
);
775 context
->change_observers()->Notify(
776 &FileChangeObserver::OnRemoveDirectory
, MakeTuple(url
));
777 return base::File::FILE_OK
;
780 webkit_blob::ScopedFile
ObfuscatedFileUtil::CreateSnapshotFile(
781 FileSystemOperationContext
* context
,
782 const FileSystemURL
& url
,
783 base::File::Error
* error
,
784 base::File::Info
* file_info
,
785 base::FilePath
* platform_path
) {
786 // We're just returning the local file information.
787 *error
= GetFileInfo(context
, url
, file_info
, platform_path
);
788 if (*error
== base::File::FILE_OK
&& file_info
->is_directory
) {
789 *file_info
= base::File::Info();
790 *error
= base::File::FILE_ERROR_NOT_A_FILE
;
792 return webkit_blob::ScopedFile();
795 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
796 ObfuscatedFileUtil::CreateFileEnumerator(
797 FileSystemOperationContext
* context
,
798 const FileSystemURL
& root_url
,
800 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(root_url
, false);
802 return scoped_ptr
<AbstractFileEnumerator
>(new EmptyFileEnumerator());
804 return scoped_ptr
<AbstractFileEnumerator
>(
805 new ObfuscatedFileEnumerator(db
, context
, this, root_url
, recursive
));
808 bool ObfuscatedFileUtil::IsDirectoryEmpty(
809 FileSystemOperationContext
* context
,
810 const FileSystemURL
& url
) {
811 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, false);
813 return true; // Not a great answer, but it's what others do.
815 if (!db
->GetFileWithPath(url
.path(), &file_id
))
816 return true; // Ditto.
818 if (!db
->GetFileInfo(file_id
, &file_info
)) {
820 // It's the root directory and the database hasn't been initialized yet.
823 if (!file_info
.is_directory())
825 std::vector
<FileId
> children
;
826 // TODO(ericu): This could easily be made faster with help from the database.
827 if (!db
->ListChildren(file_id
, &children
))
829 return children
.empty();
832 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOriginAndType(
834 const std::string
& type_string
,
836 base::File::Error
* error_code
) {
837 base::FilePath origin_dir
= GetDirectoryForOrigin(origin
, create
, error_code
);
838 if (origin_dir
.empty())
839 return base::FilePath();
840 if (type_string
.empty())
842 base::FilePath path
= origin_dir
.AppendASCII(type_string
);
843 base::File::Error error
= base::File::FILE_OK
;
844 if (!base::DirectoryExists(path
) &&
845 (!create
|| !base::CreateDirectory(path
))) {
847 base::File::FILE_ERROR_FAILED
:
848 base::File::FILE_ERROR_NOT_FOUND
;
856 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
858 const std::string
& type_string
) {
859 base::File::Error error
= base::File::FILE_OK
;
860 base::FilePath origin_type_path
= GetDirectoryForOriginAndType(
861 origin
, type_string
, false, &error
);
862 if (origin_type_path
.empty())
864 if (error
!= base::File::FILE_ERROR_NOT_FOUND
) {
865 // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase.
866 // We ignore its error now since 1) it doesn't matter the final result, and
867 // 2) it always returns false in Windows because of LevelDB's
869 // Information about failure would be useful for debugging.
870 if (!type_string
.empty())
871 DestroyDirectoryDatabase(origin
, type_string
);
872 if (!base::DeleteFile(origin_type_path
, true /* recursive */))
876 base::FilePath origin_path
= VirtualPath::DirName(origin_type_path
);
877 DCHECK_EQ(origin_path
.value(),
878 GetDirectoryForOrigin(origin
, false, NULL
).value());
880 if (!type_string
.empty()) {
881 // At this point we are sure we had successfully deleted the origin/type
882 // directory (i.e. we're ready to just return true).
883 // See if we have other directories in this origin directory.
884 for (std::set
<std::string
>::iterator iter
= known_type_strings_
.begin();
885 iter
!= known_type_strings_
.end();
887 if (*iter
== type_string
)
889 if (base::DirectoryExists(origin_path
.AppendASCII(*iter
))) {
890 // Other type's directory exists; just return true here.
896 // No other directories seem exist. Try deleting the entire origin directory.
897 InitOriginDatabase(origin
, false);
898 if (origin_database_
) {
899 origin_database_
->RemovePathForOrigin(
900 webkit_database::GetIdentifierFromOrigin(origin
));
902 if (!base::DeleteFile(origin_path
, true /* recursive */))
908 ObfuscatedFileUtil::AbstractOriginEnumerator
*
909 ObfuscatedFileUtil::CreateOriginEnumerator() {
910 std::vector
<SandboxOriginDatabase::OriginRecord
> origins
;
912 InitOriginDatabase(GURL(), false);
913 return new ObfuscatedOriginEnumerator(
914 origin_database_
.get(), file_system_directory_
);
917 bool ObfuscatedFileUtil::DestroyDirectoryDatabase(
919 const std::string
& type_string
) {
920 std::string key
= GetDirectoryDatabaseKey(origin
, type_string
);
923 DirectoryMap::iterator iter
= directories_
.find(key
);
924 if (iter
!= directories_
.end()) {
925 SandboxDirectoryDatabase
* database
= iter
->second
;
926 directories_
.erase(iter
);
930 base::File::Error error
= base::File::FILE_OK
;
931 base::FilePath path
= GetDirectoryForOriginAndType(
932 origin
, type_string
, false, &error
);
933 if (path
.empty() || error
== base::File::FILE_ERROR_NOT_FOUND
)
935 return SandboxDirectoryDatabase::DestroyDatabase(path
, env_override_
);
939 int64
ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath
& path
) {
940 return UsageForPath(VirtualPath::BaseName(path
).value().size());
943 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
944 const std::vector
<std::string
>& type_strings_to_prepopulate
) {
945 SandboxPrioritizedOriginDatabase
database(file_system_directory_
,
947 std::string origin_string
= database
.GetPrimaryOrigin();
948 if (origin_string
.empty() || !database
.HasOriginPath(origin_string
))
950 const GURL origin
= webkit_database::GetOriginFromIdentifier(origin_string
);
952 // Prepopulate the directory database(s) if and only if this instance
953 // has primary origin and the directory database is already there.
954 for (size_t i
= 0; i
< type_strings_to_prepopulate
.size(); ++i
) {
955 const std::string type_string
= type_strings_to_prepopulate
[i
];
956 // Only handles known types.
957 if (!ContainsKey(known_type_strings_
, type_string
))
959 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
960 base::FilePath path
= GetDirectoryForOriginAndType(
961 origin
, type_string
, false, &error
);
962 if (error
!= base::File::FILE_OK
)
964 scoped_ptr
<SandboxDirectoryDatabase
> db(
965 new SandboxDirectoryDatabase(path
, env_override_
));
966 if (db
->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION
)) {
967 directories_
[GetDirectoryDatabaseKey(origin
, type_string
)] = db
.release();
969 // Don't populate more than one database, as it may rather hurt
976 base::FilePath
ObfuscatedFileUtil::GetDirectoryForURL(
977 const FileSystemURL
& url
,
979 base::File::Error
* error_code
) {
980 return GetDirectoryForOriginAndType(
981 url
.origin(), CallGetTypeStringForURL(url
), create
, error_code
);
984 std::string
ObfuscatedFileUtil::CallGetTypeStringForURL(
985 const FileSystemURL
& url
) {
986 DCHECK(!get_type_string_for_url_
.is_null());
987 return get_type_string_for_url_
.Run(url
);
990 base::File::Error
ObfuscatedFileUtil::GetFileInfoInternal(
991 SandboxDirectoryDatabase
* db
,
992 FileSystemOperationContext
* context
,
993 const FileSystemURL
& url
,
995 FileInfo
* local_info
,
996 base::File::Info
* file_info
,
997 base::FilePath
* platform_file_path
) {
1001 DCHECK(platform_file_path
);
1003 if (!db
->GetFileInfo(file_id
, local_info
)) {
1005 return base::File::FILE_ERROR_FAILED
;
1008 if (local_info
->is_directory()) {
1009 file_info
->size
= 0;
1010 file_info
->is_directory
= true;
1011 file_info
->is_symbolic_link
= false;
1012 file_info
->last_modified
= local_info
->modification_time
;
1013 *platform_file_path
= base::FilePath();
1014 // We don't fill in ctime or atime.
1015 return base::File::FILE_OK
;
1017 if (local_info
->data_path
.empty())
1018 return base::File::FILE_ERROR_INVALID_OPERATION
;
1019 base::FilePath local_path
= DataPathToLocalPath(url
, local_info
->data_path
);
1020 base::File::Error error
= NativeFileUtil::GetFileInfo(
1021 local_path
, file_info
);
1022 // We should not follow symbolic links in sandboxed file system.
1023 if (base::IsLink(local_path
)) {
1024 LOG(WARNING
) << "Found a symbolic file.";
1025 error
= base::File::FILE_ERROR_NOT_FOUND
;
1027 if (error
== base::File::FILE_OK
) {
1028 *platform_file_path
= local_path
;
1029 } else if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1030 LOG(WARNING
) << "Lost a backing file.";
1031 InvalidateUsageCache(context
, url
.origin(), url
.type());
1032 if (!db
->RemoveFileInfo(file_id
))
1033 return base::File::FILE_ERROR_FAILED
;
1038 base::File
ObfuscatedFileUtil::CreateAndOpenFile(
1039 FileSystemOperationContext
* context
,
1040 const FileSystemURL
& dest_url
,
1041 FileInfo
* dest_file_info
, int file_flags
) {
1042 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1044 base::FilePath root
, dest_local_path
;
1045 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1047 if (error
!= base::File::FILE_OK
)
1048 return base::File(error
);
1050 if (base::PathExists(dest_local_path
)) {
1051 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1052 return base::File(base::File::FILE_ERROR_FAILED
);
1053 LOG(WARNING
) << "A stray file detected";
1054 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1057 base::File file
= NativeFileUtil::CreateOrOpen(dest_local_path
, file_flags
);
1058 if (!file
.IsValid())
1061 if (!file
.created()) {
1063 base::DeleteFile(dest_local_path
, false /* recursive */);
1064 return base::File(base::File::FILE_ERROR_FAILED
);
1067 error
= CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1068 if (error
!= base::File::FILE_OK
) {
1070 base::DeleteFile(dest_local_path
, false /* recursive */);
1071 return base::File(error
);
1077 base::File::Error
ObfuscatedFileUtil::CreateFile(
1078 FileSystemOperationContext
* context
,
1079 const base::FilePath
& src_file_path
,
1080 const FileSystemURL
& dest_url
,
1081 FileInfo
* dest_file_info
) {
1082 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(dest_url
, true);
1084 base::FilePath root
, dest_local_path
;
1085 base::File::Error error
= GenerateNewLocalPath(db
, context
, dest_url
, &root
,
1087 if (error
!= base::File::FILE_OK
)
1090 bool created
= false;
1091 if (src_file_path
.empty()) {
1092 if (base::PathExists(dest_local_path
)) {
1093 if (!base::DeleteFile(dest_local_path
, true /* recursive */))
1094 return base::File::FILE_ERROR_FAILED
;
1095 LOG(WARNING
) << "A stray file detected";
1096 InvalidateUsageCache(context
, dest_url
.origin(), dest_url
.type());
1099 error
= NativeFileUtil::EnsureFileExists(dest_local_path
, &created
);
1101 error
= NativeFileUtil::CopyOrMoveFile(
1102 src_file_path
, dest_local_path
,
1103 FileSystemOperation::OPTION_NONE
,
1104 fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url
,
1108 if (error
!= base::File::FILE_OK
)
1111 return base::File::FILE_ERROR_FAILED
;
1113 return CommitCreateFile(root
, dest_local_path
, db
, dest_file_info
);
1116 base::File::Error
ObfuscatedFileUtil::CommitCreateFile(
1117 const base::FilePath
& root
,
1118 const base::FilePath
& local_path
,
1119 SandboxDirectoryDatabase
* db
,
1120 FileInfo
* dest_file_info
) {
1121 // This removes the root, including the trailing slash, leaving a relative
1123 dest_file_info
->data_path
= base::FilePath(
1124 local_path
.value().substr(root
.value().length() + 1));
1127 base::File::Error error
= db
->AddFileInfo(*dest_file_info
, &file_id
);
1128 if (error
!= base::File::FILE_OK
)
1131 TouchDirectory(db
, dest_file_info
->parent_id
);
1132 return base::File::FILE_OK
;
1135 base::FilePath
ObfuscatedFileUtil::DataPathToLocalPath(
1136 const FileSystemURL
& url
, const base::FilePath
& data_path
) {
1137 base::File::Error error
= base::File::FILE_OK
;
1138 base::FilePath root
= GetDirectoryForURL(url
, false, &error
);
1139 if (error
!= base::File::FILE_OK
)
1140 return base::FilePath();
1141 return root
.Append(data_path
);
1144 std::string
ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1145 const GURL
& origin
, const std::string
& type_string
) {
1146 if (type_string
.empty()) {
1147 LOG(WARNING
) << "Unknown filesystem type requested:" << type_string
;
1148 return std::string();
1150 // For isolated origin we just use a type string as a key.
1151 return webkit_database::GetIdentifierFromOrigin(origin
) +
1155 // TODO(ericu): How to do the whole validation-without-creation thing?
1156 // We may not have quota even to create the database.
1157 // Ah, in that case don't even get here?
1158 // Still doesn't answer the quota issue, though.
1159 SandboxDirectoryDatabase
* ObfuscatedFileUtil::GetDirectoryDatabase(
1160 const FileSystemURL
& url
, bool create
) {
1161 std::string key
= GetDirectoryDatabaseKey(
1162 url
.origin(), CallGetTypeStringForURL(url
));
1166 DirectoryMap::iterator iter
= directories_
.find(key
);
1167 if (iter
!= directories_
.end()) {
1169 return iter
->second
;
1172 base::File::Error error
= base::File::FILE_OK
;
1173 base::FilePath path
= GetDirectoryForURL(url
, create
, &error
);
1174 if (error
!= base::File::FILE_OK
) {
1175 LOG(WARNING
) << "Failed to get origin+type directory: "
1176 << url
.DebugString() << " error:" << error
;
1180 SandboxDirectoryDatabase
* database
=
1181 new SandboxDirectoryDatabase(path
, env_override_
);
1182 directories_
[key
] = database
;
1186 base::FilePath
ObfuscatedFileUtil::GetDirectoryForOrigin(
1187 const GURL
& origin
, bool create
, base::File::Error
* error_code
) {
1188 if (!InitOriginDatabase(origin
, create
)) {
1190 *error_code
= create
?
1191 base::File::FILE_ERROR_FAILED
:
1192 base::File::FILE_ERROR_NOT_FOUND
;
1194 return base::FilePath();
1196 base::FilePath directory_name
;
1197 std::string id
= webkit_database::GetIdentifierFromOrigin(origin
);
1199 bool exists_in_db
= origin_database_
->HasOriginPath(id
);
1200 if (!exists_in_db
&& !create
) {
1202 *error_code
= base::File::FILE_ERROR_NOT_FOUND
;
1203 return base::FilePath();
1205 if (!origin_database_
->GetPathForOrigin(id
, &directory_name
)) {
1207 *error_code
= base::File::FILE_ERROR_FAILED
;
1208 return base::FilePath();
1211 base::FilePath path
= file_system_directory_
.Append(directory_name
);
1212 bool exists_in_fs
= base::DirectoryExists(path
);
1213 if (!exists_in_db
&& exists_in_fs
) {
1214 if (!base::DeleteFile(path
, true)) {
1216 *error_code
= base::File::FILE_ERROR_FAILED
;
1217 return base::FilePath();
1219 exists_in_fs
= false;
1222 if (!exists_in_fs
) {
1223 if (!create
|| !base::CreateDirectory(path
)) {
1225 *error_code
= create
?
1226 base::File::FILE_ERROR_FAILED
:
1227 base::File::FILE_ERROR_NOT_FOUND
;
1228 return base::FilePath();
1233 *error_code
= base::File::FILE_OK
;
1238 void ObfuscatedFileUtil::InvalidateUsageCache(
1239 FileSystemOperationContext
* context
,
1241 FileSystemType type
) {
1242 if (sandbox_delegate_
)
1243 sandbox_delegate_
->InvalidateUsageCache(origin
, type
);
1246 void ObfuscatedFileUtil::MarkUsed() {
1248 timer_
.reset(new TimedTaskHelper(file_task_runner_
.get()));
1250 if (timer_
->IsRunning()) {
1253 timer_
->Start(FROM_HERE
,
1254 base::TimeDelta::FromSeconds(db_flush_delay_seconds_
),
1255 base::Bind(&ObfuscatedFileUtil::DropDatabases
,
1256 base::Unretained(this)));
1260 void ObfuscatedFileUtil::DropDatabases() {
1261 origin_database_
.reset();
1262 STLDeleteContainerPairSecondPointers(
1263 directories_
.begin(), directories_
.end());
1264 directories_
.clear();
1268 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL
& origin_hint
,
1270 if (origin_database_
)
1273 if (!create
&& !base::DirectoryExists(file_system_directory_
))
1275 if (!base::CreateDirectory(file_system_directory_
)) {
1276 LOG(WARNING
) << "Failed to create FileSystem directory: " <<
1277 file_system_directory_
.value();
1281 SandboxPrioritizedOriginDatabase
* prioritized_origin_database
=
1282 new SandboxPrioritizedOriginDatabase(file_system_directory_
,
1284 origin_database_
.reset(prioritized_origin_database
);
1286 if (origin_hint
.is_empty() || !HasIsolatedStorage(origin_hint
))
1289 const std::string isolated_origin_string
=
1290 webkit_database::GetIdentifierFromOrigin(origin_hint
);
1292 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1293 base::FilePath isolated_origin_dir
= file_system_directory_
.Append(
1294 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory
);
1295 if (base::DirectoryExists(isolated_origin_dir
) &&
1296 prioritized_origin_database
->GetSandboxOriginDatabase()) {
1297 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1298 isolated_origin_string
,
1299 file_system_directory_
,
1300 prioritized_origin_database
->GetSandboxOriginDatabase());
1303 prioritized_origin_database
->InitializePrimaryOrigin(
1304 isolated_origin_string
);
1309 base::File::Error
ObfuscatedFileUtil::GenerateNewLocalPath(
1310 SandboxDirectoryDatabase
* db
,
1311 FileSystemOperationContext
* context
,
1312 const FileSystemURL
& url
,
1313 base::FilePath
* root
,
1314 base::FilePath
* local_path
) {
1317 if (!db
|| !db
->GetNextInteger(&number
))
1318 return base::File::FILE_ERROR_FAILED
;
1320 base::File::Error error
= base::File::FILE_OK
;
1321 *root
= GetDirectoryForURL(url
, false, &error
);
1322 if (error
!= base::File::FILE_OK
)
1325 // We use the third- and fourth-to-last digits as the directory.
1326 int64 directory_number
= number
% 10000 / 100;
1327 base::FilePath new_local_path
= root
->AppendASCII(
1328 base::StringPrintf("%02" PRId64
, directory_number
));
1330 error
= NativeFileUtil::CreateDirectory(
1331 new_local_path
, false /* exclusive */, false /* recursive */);
1332 if (error
!= base::File::FILE_OK
)
1336 new_local_path
.AppendASCII(base::StringPrintf("%08" PRId64
, number
));
1337 return base::File::FILE_OK
;
1340 base::File
ObfuscatedFileUtil::CreateOrOpenInternal(
1341 FileSystemOperationContext
* context
,
1342 const FileSystemURL
& url
, int file_flags
) {
1343 DCHECK(!(file_flags
& (base::File::FLAG_DELETE_ON_CLOSE
|
1344 base::File::FLAG_HIDDEN
| base::File::FLAG_EXCLUSIVE_READ
|
1345 base::File::FLAG_EXCLUSIVE_WRITE
)));
1346 SandboxDirectoryDatabase
* db
= GetDirectoryDatabase(url
, true);
1348 return base::File(base::File::FILE_ERROR_FAILED
);
1350 if (!db
->GetFileWithPath(url
.path(), &file_id
)) {
1351 // The file doesn't exist.
1352 if (!(file_flags
& (base::File::FLAG_CREATE
|
1353 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_OPEN_ALWAYS
))) {
1354 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1357 if (!db
->GetFileWithPath(VirtualPath::DirName(url
.path()), &parent_id
))
1358 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
1360 InitFileInfo(&file_info
, parent_id
,
1361 VirtualPath::BaseName(url
.path()).value());
1363 int64 growth
= UsageForPath(file_info
.name
.size());
1364 if (!AllocateQuota(context
, growth
))
1365 return base::File(base::File::FILE_ERROR_NO_SPACE
);
1366 base::File file
= CreateAndOpenFile(context
, url
, &file_info
, file_flags
);
1367 if (file
.IsValid()) {
1368 UpdateUsage(context
, url
, growth
);
1369 context
->change_observers()->Notify(
1370 &FileChangeObserver::OnCreateFile
, MakeTuple(url
));
1375 if (file_flags
& base::File::FLAG_CREATE
)
1376 return base::File(base::File::FILE_ERROR_EXISTS
);
1378 base::File::Info platform_file_info
;
1379 base::FilePath local_path
;
1381 base::File::Error error
= GetFileInfoInternal(
1382 db
, context
, url
, file_id
, &file_info
, &platform_file_info
, &local_path
);
1383 if (error
!= base::File::FILE_OK
)
1384 return base::File(error
);
1385 if (file_info
.is_directory())
1386 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
1389 if (file_flags
& (base::File::FLAG_CREATE_ALWAYS
|
1390 base::File::FLAG_OPEN_TRUNCATED
)) {
1391 // The file exists and we're truncating.
1392 delta
= -platform_file_info
.size
;
1393 AllocateQuota(context
, delta
);
1396 base::File file
= NativeFileUtil::CreateOrOpen(local_path
, file_flags
);
1397 if (!file
.IsValid()) {
1398 error
= file
.error_details();
1399 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
1400 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1401 // TODO(tzik): Delete database entry after ensuring the file lost.
1402 InvalidateUsageCache(context
, url
.origin(), url
.type());
1403 LOG(WARNING
) << "Lost a backing file.";
1404 return base::File(base::File::FILE_ERROR_FAILED
);
1409 // If truncating we need to update the usage.
1411 UpdateUsage(context
, url
, delta
);
1412 context
->change_observers()->Notify(
1413 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
1418 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL
& origin
) {
1419 return special_storage_policy_
.get() &&
1420 special_storage_policy_
->HasIsolatedStorage(origin
);
1423 } // namespace fileapi