- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / obfuscated_file_util.cc
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.
4
5 #include "webkit/browser/fileapi/obfuscated_file_util.h"
6
7 #include <queue>
8 #include <string>
9 #include <vector>
10
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"
22 #include "url/gurl.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"
36
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);
41 //
42 //     NativeFileUtil::DoSomething(local_path);
43 //     file_util::DoAnother(local_path);
44 //  }
45
46 namespace fileapi {
47
48 namespace {
49
50 typedef SandboxDirectoryDatabase::FileId FileId;
51 typedef SandboxDirectoryDatabase::FileInfo FileInfo;
52
53 void InitFileInfo(
54     SandboxDirectoryDatabase::FileInfo* file_info,
55     SandboxDirectoryDatabase::FileId parent_id,
56     const base::FilePath::StringType& file_name) {
57   DCHECK(file_info);
58   file_info->parent_id = parent_id;
59   file_info->name = file_name;
60 }
61
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.
69
70 int64 UsageForPath(size_t length) {
71   return kPathCreationQuotaCost +
72       static_cast<int64>(length) * kPathByteQuotaCost;
73 }
74
75 bool AllocateQuota(FileSystemOperationContext* context, int64 growth) {
76   if (context->allowed_bytes_growth() == quota::QuotaManager::kNoLimit)
77     return true;
78
79   int64 new_quota = context->allowed_bytes_growth() - growth;
80   if (growth > 0 && new_quota < 0)
81     return false;
82   context->set_allowed_bytes_growth(new_quota);
83   return true;
84 }
85
86 void UpdateUsage(
87     FileSystemOperationContext* context,
88     const FileSystemURL& url,
89     int64 growth) {
90   context->update_observers()->Notify(
91       &FileUpdateObserver::OnUpdate, MakeTuple(url, growth));
92 }
93
94 void TouchDirectory(SandboxDirectoryDatabase* db, FileId dir_id) {
95   DCHECK(db);
96   if (!db->UpdateModificationTime(dir_id, base::Time::Now()))
97     NOTREACHED();
98 }
99
100 enum IsolatedOriginStatus {
101   kIsolatedOriginMatch,
102   kIsolatedOriginDontMatch,
103   kIsolatedOriginStatusMax,
104 };
105
106 }  // namespace
107
108 using base::PlatformFile;
109 using base::PlatformFileError;
110
111 class ObfuscatedFileEnumerator
112     : public FileSystemFileUtil::AbstractFileEnumerator {
113  public:
114   ObfuscatedFileEnumerator(
115       SandboxDirectoryDatabase* db,
116       FileSystemOperationContext* context,
117       ObfuscatedFileUtil* obfuscated_file_util,
118       const FileSystemURL& root_url,
119       bool recursive)
120       : db_(db),
121         context_(context),
122         obfuscated_file_util_(obfuscated_file_util),
123         root_url_(root_url),
124         recursive_(recursive),
125         current_file_id_(0) {
126     base::FilePath root_virtual_path = root_url.path();
127     FileId file_id;
128
129     if (!db_->GetFileWithPath(root_virtual_path, &file_id))
130       return;
131
132     FileRecord record = { file_id, root_virtual_path };
133     recurse_queue_.push(record);
134   }
135
136   virtual ~ObfuscatedFileEnumerator() {}
137
138   virtual base::FilePath Next() OVERRIDE {
139     ProcessRecurseQueue();
140     if (display_stack_.empty())
141       return base::FilePath();
142
143     current_file_id_ = display_stack_.back();
144     display_stack_.pop_back();
145
146     FileInfo file_info;
147     base::FilePath platform_file_path;
148     base::PlatformFileError error =
149         obfuscated_file_util_->GetFileInfoInternal(
150             db_, context_, root_url_, current_file_id_,
151             &file_info, &current_platform_file_info_, &platform_file_path);
152     if (error != base::PLATFORM_FILE_OK)
153       return Next();
154
155     base::FilePath virtual_path =
156         current_parent_virtual_path_.Append(file_info.name);
157     if (recursive_ && file_info.is_directory()) {
158       FileRecord record = { current_file_id_, virtual_path };
159       recurse_queue_.push(record);
160     }
161     return virtual_path;
162   }
163
164   virtual int64 Size() OVERRIDE {
165     return current_platform_file_info_.size;
166   }
167
168   virtual base::Time LastModifiedTime() OVERRIDE {
169     return current_platform_file_info_.last_modified;
170   }
171
172   virtual bool IsDirectory() OVERRIDE {
173     return current_platform_file_info_.is_directory;
174   }
175
176  private:
177   typedef SandboxDirectoryDatabase::FileId FileId;
178   typedef SandboxDirectoryDatabase::FileInfo FileInfo;
179
180   struct FileRecord {
181     FileId file_id;
182     base::FilePath virtual_path;
183   };
184
185   void ProcessRecurseQueue() {
186     while (display_stack_.empty() && !recurse_queue_.empty()) {
187       FileRecord entry = recurse_queue_.front();
188       recurse_queue_.pop();
189       if (!db_->ListChildren(entry.file_id, &display_stack_)) {
190         display_stack_.clear();
191         return;
192       }
193       current_parent_virtual_path_ = entry.virtual_path;
194     }
195   }
196
197   SandboxDirectoryDatabase* db_;
198   FileSystemOperationContext* context_;
199   ObfuscatedFileUtil* obfuscated_file_util_;
200   FileSystemURL root_url_;
201   bool recursive_;
202
203   std::queue<FileRecord> recurse_queue_;
204   std::vector<FileId> display_stack_;
205   base::FilePath current_parent_virtual_path_;
206
207   FileId current_file_id_;
208   base::PlatformFileInfo current_platform_file_info_;
209 };
210
211 class ObfuscatedOriginEnumerator
212     : public ObfuscatedFileUtil::AbstractOriginEnumerator {
213  public:
214   typedef SandboxOriginDatabase::OriginRecord OriginRecord;
215   ObfuscatedOriginEnumerator(
216       SandboxOriginDatabaseInterface* origin_database,
217       const base::FilePath& base_file_path)
218       : base_file_path_(base_file_path) {
219     if (origin_database)
220       origin_database->ListAllOrigins(&origins_);
221   }
222
223   virtual ~ObfuscatedOriginEnumerator() {}
224
225   // Returns the next origin.  Returns empty if there are no more origins.
226   virtual GURL Next() OVERRIDE {
227     OriginRecord record;
228     if (!origins_.empty()) {
229       record = origins_.back();
230       origins_.pop_back();
231     }
232     current_ = record;
233     return webkit_database::GetOriginFromIdentifier(record.origin);
234   }
235
236   // Returns the current origin's information.
237   virtual bool HasTypeDirectory(const std::string& type_string) const OVERRIDE {
238     if (current_.path.empty())
239       return false;
240     if (type_string.empty()) {
241       NOTREACHED();
242       return false;
243     }
244     base::FilePath path =
245         base_file_path_.Append(current_.path).AppendASCII(type_string);
246     return base::DirectoryExists(path);
247   }
248
249  private:
250   std::vector<OriginRecord> origins_;
251   OriginRecord current_;
252   base::FilePath base_file_path_;
253 };
254
255 ObfuscatedFileUtil::ObfuscatedFileUtil(
256     quota::SpecialStoragePolicy* special_storage_policy,
257     const base::FilePath& file_system_directory,
258     base::SequencedTaskRunner* file_task_runner,
259     const GetTypeStringForURLCallback& get_type_string_for_url,
260     const std::set<std::string>& known_type_strings,
261     SandboxFileSystemBackendDelegate* sandbox_delegate)
262     : special_storage_policy_(special_storage_policy),
263       file_system_directory_(file_system_directory),
264       db_flush_delay_seconds_(10 * 60),  // 10 mins.
265       file_task_runner_(file_task_runner),
266       get_type_string_for_url_(get_type_string_for_url),
267       known_type_strings_(known_type_strings),
268       sandbox_delegate_(sandbox_delegate) {
269 }
270
271 ObfuscatedFileUtil::~ObfuscatedFileUtil() {
272   DropDatabases();
273 }
274
275 PlatformFileError ObfuscatedFileUtil::CreateOrOpen(
276     FileSystemOperationContext* context,
277     const FileSystemURL& url, int file_flags,
278     PlatformFile* file_handle, bool* created) {
279   PlatformFileError error = CreateOrOpenInternal(context, url, file_flags,
280                                                  file_handle, created);
281   if (*file_handle != base::kInvalidPlatformFileValue &&
282       file_flags & base::PLATFORM_FILE_WRITE &&
283       context->quota_limit_type() == quota::kQuotaLimitTypeUnlimited &&
284       sandbox_delegate_) {
285     DCHECK_EQ(base::PLATFORM_FILE_OK, error);
286     sandbox_delegate_->StickyInvalidateUsageCache(url.origin(), url.type());
287   }
288   return error;
289 }
290
291 PlatformFileError ObfuscatedFileUtil::Close(
292     FileSystemOperationContext* context,
293     base::PlatformFile file) {
294   return NativeFileUtil::Close(file);
295 }
296
297 PlatformFileError ObfuscatedFileUtil::EnsureFileExists(
298     FileSystemOperationContext* context,
299     const FileSystemURL& url,
300     bool* created) {
301   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
302   if (!db)
303     return base::PLATFORM_FILE_ERROR_FAILED;
304
305   FileId file_id;
306   if (db->GetFileWithPath(url.path(), &file_id)) {
307     FileInfo file_info;
308     if (!db->GetFileInfo(file_id, &file_info)) {
309       NOTREACHED();
310       return base::PLATFORM_FILE_ERROR_FAILED;
311     }
312     if (file_info.is_directory())
313       return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
314     if (created)
315       *created = false;
316     return base::PLATFORM_FILE_OK;
317   }
318   FileId parent_id;
319   if (!db->GetFileWithPath(VirtualPath::DirName(url.path()), &parent_id))
320     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
321
322   FileInfo file_info;
323   InitFileInfo(&file_info, parent_id,
324                VirtualPath::BaseName(url.path()).value());
325
326   int64 growth = UsageForPath(file_info.name.size());
327   if (!AllocateQuota(context, growth))
328     return base::PLATFORM_FILE_ERROR_NO_SPACE;
329   PlatformFileError error = CreateFile(
330       context, base::FilePath(), url, &file_info, 0, NULL);
331   if (created && base::PLATFORM_FILE_OK == error) {
332     *created = true;
333     UpdateUsage(context, url, growth);
334     context->change_observers()->Notify(
335         &FileChangeObserver::OnCreateFile, MakeTuple(url));
336   }
337   return error;
338 }
339
340 PlatformFileError ObfuscatedFileUtil::CreateDirectory(
341     FileSystemOperationContext* context,
342     const FileSystemURL& url,
343     bool exclusive,
344     bool recursive) {
345   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
346   if (!db)
347     return base::PLATFORM_FILE_ERROR_FAILED;
348
349   FileId file_id;
350   if (db->GetFileWithPath(url.path(), &file_id)) {
351     FileInfo file_info;
352     if (exclusive)
353       return base::PLATFORM_FILE_ERROR_EXISTS;
354     if (!db->GetFileInfo(file_id, &file_info)) {
355       NOTREACHED();
356       return base::PLATFORM_FILE_ERROR_FAILED;
357     }
358     if (!file_info.is_directory())
359       return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
360     return base::PLATFORM_FILE_OK;
361   }
362
363   std::vector<base::FilePath::StringType> components;
364   VirtualPath::GetComponents(url.path(), &components);
365   FileId parent_id = 0;
366   size_t index;
367   for (index = 0; index < components.size(); ++index) {
368     base::FilePath::StringType name = components[index];
369     if (name == FILE_PATH_LITERAL("/"))
370       continue;
371     if (!db->GetChildWithName(parent_id, name, &parent_id))
372       break;
373   }
374   if (!recursive && components.size() - index > 1)
375     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
376   bool first = true;
377   for (; index < components.size(); ++index) {
378     FileInfo file_info;
379     file_info.name = components[index];
380     if (file_info.name == FILE_PATH_LITERAL("/"))
381       continue;
382     file_info.modification_time = base::Time::Now();
383     file_info.parent_id = parent_id;
384     int64 growth = UsageForPath(file_info.name.size());
385     if (!AllocateQuota(context, growth))
386       return base::PLATFORM_FILE_ERROR_NO_SPACE;
387     base::PlatformFileError error = db->AddFileInfo(file_info, &parent_id);
388     if (error != base::PLATFORM_FILE_OK)
389       return error;
390     UpdateUsage(context, url, growth);
391     context->change_observers()->Notify(
392         &FileChangeObserver::OnCreateDirectory, MakeTuple(url));
393     if (first) {
394       first = false;
395       TouchDirectory(db, file_info.parent_id);
396     }
397   }
398   return base::PLATFORM_FILE_OK;
399 }
400
401 PlatformFileError ObfuscatedFileUtil::GetFileInfo(
402     FileSystemOperationContext* context,
403     const FileSystemURL& url,
404     base::PlatformFileInfo* file_info,
405     base::FilePath* platform_file_path) {
406   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
407   if (!db)
408     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
409   FileId file_id;
410   if (!db->GetFileWithPath(url.path(), &file_id))
411     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
412   FileInfo local_info;
413   return GetFileInfoInternal(db, context, url,
414                              file_id, &local_info,
415                              file_info, platform_file_path);
416 }
417
418 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
419     ObfuscatedFileUtil::CreateFileEnumerator(
420     FileSystemOperationContext* context,
421     const FileSystemURL& root_url) {
422   return CreateFileEnumerator(context, root_url, false /* recursive */);
423 }
424
425 PlatformFileError ObfuscatedFileUtil::GetLocalFilePath(
426     FileSystemOperationContext* context,
427     const FileSystemURL& url,
428     base::FilePath* local_path) {
429   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
430   if (!db)
431     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
432   FileId file_id;
433   if (!db->GetFileWithPath(url.path(), &file_id))
434     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
435   FileInfo file_info;
436   if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) {
437     NOTREACHED();
438     // Directories have no local file path.
439     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
440   }
441   *local_path = DataPathToLocalPath(url, file_info.data_path);
442
443   if (local_path->empty())
444     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
445   return base::PLATFORM_FILE_OK;
446 }
447
448 PlatformFileError ObfuscatedFileUtil::Touch(
449     FileSystemOperationContext* context,
450     const FileSystemURL& url,
451     const base::Time& last_access_time,
452     const base::Time& last_modified_time) {
453   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
454   if (!db)
455     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
456   FileId file_id;
457   if (!db->GetFileWithPath(url.path(), &file_id))
458     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
459
460   FileInfo file_info;
461   if (!db->GetFileInfo(file_id, &file_info)) {
462     NOTREACHED();
463     return base::PLATFORM_FILE_ERROR_FAILED;
464   }
465   if (file_info.is_directory()) {
466     if (!db->UpdateModificationTime(file_id, last_modified_time))
467       return base::PLATFORM_FILE_ERROR_FAILED;
468     return base::PLATFORM_FILE_OK;
469   }
470   return NativeFileUtil::Touch(
471       DataPathToLocalPath(url, file_info.data_path),
472       last_access_time, last_modified_time);
473 }
474
475 PlatformFileError ObfuscatedFileUtil::Truncate(
476     FileSystemOperationContext* context,
477     const FileSystemURL& url,
478     int64 length) {
479   base::PlatformFileInfo file_info;
480   base::FilePath local_path;
481   base::PlatformFileError error =
482       GetFileInfo(context, url, &file_info, &local_path);
483   if (error != base::PLATFORM_FILE_OK)
484     return error;
485
486   int64 growth = length - file_info.size;
487   if (!AllocateQuota(context, growth))
488     return base::PLATFORM_FILE_ERROR_NO_SPACE;
489   error = NativeFileUtil::Truncate(local_path, length);
490   if (error == base::PLATFORM_FILE_OK) {
491     UpdateUsage(context, url, growth);
492     context->change_observers()->Notify(
493         &FileChangeObserver::OnModifyFile, MakeTuple(url));
494   }
495   return error;
496 }
497
498 PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile(
499     FileSystemOperationContext* context,
500     const FileSystemURL& src_url,
501     const FileSystemURL& dest_url,
502     CopyOrMoveOption option,
503     bool copy) {
504   // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
505   DCHECK(src_url.origin() == dest_url.origin());
506   DCHECK(src_url.type() == dest_url.type());
507
508   SandboxDirectoryDatabase* db = GetDirectoryDatabase(src_url, true);
509   if (!db)
510     return base::PLATFORM_FILE_ERROR_FAILED;
511
512   FileId src_file_id;
513   if (!db->GetFileWithPath(src_url.path(), &src_file_id))
514     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
515
516   FileId dest_file_id;
517   bool overwrite = db->GetFileWithPath(dest_url.path(),
518                                        &dest_file_id);
519
520   FileInfo src_file_info;
521   base::PlatformFileInfo src_platform_file_info;
522   base::FilePath src_local_path;
523   base::PlatformFileError error = GetFileInfoInternal(
524       db, context, src_url, src_file_id,
525       &src_file_info, &src_platform_file_info, &src_local_path);
526   if (error != base::PLATFORM_FILE_OK)
527     return error;
528   if (src_file_info.is_directory())
529     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
530
531   FileInfo dest_file_info;
532   base::PlatformFileInfo dest_platform_file_info;  // overwrite case only
533   base::FilePath dest_local_path;  // overwrite case only
534   if (overwrite) {
535     base::PlatformFileError error = GetFileInfoInternal(
536         db, context, dest_url, dest_file_id,
537         &dest_file_info, &dest_platform_file_info, &dest_local_path);
538     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
539       overwrite = false;  // fallback to non-overwrite case
540     else if (error != base::PLATFORM_FILE_OK)
541       return error;
542     else if (dest_file_info.is_directory())
543       return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
544   }
545   if (!overwrite) {
546     FileId dest_parent_id;
547     if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()),
548                              &dest_parent_id)) {
549       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
550     }
551
552     dest_file_info = src_file_info;
553     dest_file_info.parent_id = dest_parent_id;
554     dest_file_info.name =
555         VirtualPath::BaseName(dest_url.path()).value();
556   }
557
558   int64 growth = 0;
559   if (copy)
560     growth += src_platform_file_info.size;
561   else
562     growth -= UsageForPath(src_file_info.name.size());
563   if (overwrite)
564     growth -= dest_platform_file_info.size;
565   else
566     growth += UsageForPath(dest_file_info.name.size());
567   if (!AllocateQuota(context, growth))
568     return base::PLATFORM_FILE_ERROR_NO_SPACE;
569
570   /*
571    * Copy-with-overwrite
572    *  Just overwrite data file
573    * Copy-without-overwrite
574    *  Copy backing file
575    *  Create new metadata pointing to new backing file.
576    * Move-with-overwrite
577    *  transaction:
578    *    Remove source entry.
579    *    Point target entry to source entry's backing file.
580    *  Delete target entry's old backing file
581    * Move-without-overwrite
582    *  Just update metadata
583    */
584   error = base::PLATFORM_FILE_ERROR_FAILED;
585   if (copy) {
586     if (overwrite) {
587       error = NativeFileUtil::CopyOrMoveFile(
588           src_local_path,
589           dest_local_path,
590           option,
591           true /* copy */);
592     } else {  // non-overwrite
593       error = CreateFile(context, src_local_path,
594                          dest_url, &dest_file_info, 0, NULL);
595     }
596   } else {
597     if (overwrite) {
598       if (db->OverwritingMoveFile(src_file_id, dest_file_id)) {
599         if (base::PLATFORM_FILE_OK !=
600             NativeFileUtil::DeleteFile(dest_local_path))
601           LOG(WARNING) << "Leaked a backing file.";
602         error = base::PLATFORM_FILE_OK;
603       } else {
604         error = base::PLATFORM_FILE_ERROR_FAILED;
605       }
606     } else {  // non-overwrite
607       if (db->UpdateFileInfo(src_file_id, dest_file_info))
608         error = base::PLATFORM_FILE_OK;
609       else
610         error = base::PLATFORM_FILE_ERROR_FAILED;
611     }
612   }
613
614   if (error != base::PLATFORM_FILE_OK)
615     return error;
616
617   if (overwrite) {
618     context->change_observers()->Notify(
619         &FileChangeObserver::OnModifyFile,
620         MakeTuple(dest_url));
621   } else {
622     context->change_observers()->Notify(
623         &FileChangeObserver::OnCreateFileFrom,
624         MakeTuple(dest_url, src_url));
625   }
626
627   if (!copy) {
628     context->change_observers()->Notify(
629         &FileChangeObserver::OnRemoveFile, MakeTuple(src_url));
630     TouchDirectory(db, src_file_info.parent_id);
631   }
632
633   TouchDirectory(db, dest_file_info.parent_id);
634
635   UpdateUsage(context, dest_url, growth);
636   return error;
637 }
638
639 PlatformFileError ObfuscatedFileUtil::CopyInForeignFile(
640     FileSystemOperationContext* context,
641     const base::FilePath& src_file_path,
642     const FileSystemURL& dest_url) {
643   SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true);
644   if (!db)
645     return base::PLATFORM_FILE_ERROR_FAILED;
646
647   base::PlatformFileInfo src_platform_file_info;
648   if (!file_util::GetFileInfo(src_file_path, &src_platform_file_info))
649     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
650
651   FileId dest_file_id;
652   bool overwrite = db->GetFileWithPath(dest_url.path(),
653                                        &dest_file_id);
654
655   FileInfo dest_file_info;
656   base::PlatformFileInfo dest_platform_file_info;  // overwrite case only
657   if (overwrite) {
658     base::FilePath dest_local_path;
659     base::PlatformFileError error = GetFileInfoInternal(
660         db, context, dest_url, dest_file_id,
661         &dest_file_info, &dest_platform_file_info, &dest_local_path);
662     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
663       overwrite = false;  // fallback to non-overwrite case
664     else if (error != base::PLATFORM_FILE_OK)
665       return error;
666     else if (dest_file_info.is_directory())
667       return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
668   }
669   if (!overwrite) {
670     FileId dest_parent_id;
671     if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()),
672                              &dest_parent_id)) {
673       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
674     }
675     if (!dest_file_info.is_directory())
676       return base::PLATFORM_FILE_ERROR_FAILED;
677     InitFileInfo(&dest_file_info, dest_parent_id,
678                  VirtualPath::BaseName(dest_url.path()).value());
679   }
680
681   int64 growth = src_platform_file_info.size;
682   if (overwrite)
683     growth -= dest_platform_file_info.size;
684   else
685     growth += UsageForPath(dest_file_info.name.size());
686   if (!AllocateQuota(context, growth))
687     return base::PLATFORM_FILE_ERROR_NO_SPACE;
688
689   base::PlatformFileError error;
690   if (overwrite) {
691     base::FilePath dest_local_path =
692         DataPathToLocalPath(dest_url, dest_file_info.data_path);
693     error = NativeFileUtil::CopyOrMoveFile(
694         src_file_path, dest_local_path,
695         FileSystemOperation::OPTION_NONE, true);
696   } else {
697     error = CreateFile(context, src_file_path,
698                        dest_url, &dest_file_info, 0, NULL);
699   }
700
701   if (error != base::PLATFORM_FILE_OK)
702     return error;
703
704   if (overwrite) {
705     context->change_observers()->Notify(
706         &FileChangeObserver::OnModifyFile, MakeTuple(dest_url));
707   } else {
708     context->change_observers()->Notify(
709         &FileChangeObserver::OnCreateFile, MakeTuple(dest_url));
710   }
711
712   UpdateUsage(context, dest_url, growth);
713   TouchDirectory(db, dest_file_info.parent_id);
714   return base::PLATFORM_FILE_OK;
715 }
716
717 PlatformFileError ObfuscatedFileUtil::DeleteFile(
718     FileSystemOperationContext* context,
719     const FileSystemURL& url) {
720   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
721   if (!db)
722     return base::PLATFORM_FILE_ERROR_FAILED;
723   FileId file_id;
724   if (!db->GetFileWithPath(url.path(), &file_id))
725     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
726
727   FileInfo file_info;
728   base::PlatformFileInfo platform_file_info;
729   base::FilePath local_path;
730   base::PlatformFileError error = GetFileInfoInternal(
731       db, context, url, file_id, &file_info, &platform_file_info, &local_path);
732   if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND &&
733       error != base::PLATFORM_FILE_OK)
734     return error;
735
736   if (file_info.is_directory())
737     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
738
739   int64 growth = -UsageForPath(file_info.name.size()) - platform_file_info.size;
740   AllocateQuota(context, growth);
741   if (!db->RemoveFileInfo(file_id)) {
742     NOTREACHED();
743     return base::PLATFORM_FILE_ERROR_FAILED;
744   }
745   UpdateUsage(context, url, growth);
746   TouchDirectory(db, file_info.parent_id);
747
748   context->change_observers()->Notify(
749       &FileChangeObserver::OnRemoveFile, MakeTuple(url));
750
751   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
752     return base::PLATFORM_FILE_OK;
753
754   error = NativeFileUtil::DeleteFile(local_path);
755   if (base::PLATFORM_FILE_OK != error)
756     LOG(WARNING) << "Leaked a backing file.";
757   return base::PLATFORM_FILE_OK;
758 }
759
760 PlatformFileError ObfuscatedFileUtil::DeleteDirectory(
761     FileSystemOperationContext* context,
762     const FileSystemURL& url) {
763   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
764   if (!db)
765     return base::PLATFORM_FILE_ERROR_FAILED;
766
767   FileId file_id;
768   if (!db->GetFileWithPath(url.path(), &file_id))
769     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
770   FileInfo file_info;
771   if (!db->GetFileInfo(file_id, &file_info)) {
772     NOTREACHED();
773     return base::PLATFORM_FILE_ERROR_FAILED;
774   }
775   if (!file_info.is_directory())
776     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
777   if (!db->RemoveFileInfo(file_id))
778     return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
779   int64 growth = -UsageForPath(file_info.name.size());
780   AllocateQuota(context, growth);
781   UpdateUsage(context, url, growth);
782   TouchDirectory(db, file_info.parent_id);
783   context->change_observers()->Notify(
784       &FileChangeObserver::OnRemoveDirectory, MakeTuple(url));
785   return base::PLATFORM_FILE_OK;
786 }
787
788 webkit_blob::ScopedFile ObfuscatedFileUtil::CreateSnapshotFile(
789     FileSystemOperationContext* context,
790     const FileSystemURL& url,
791     base::PlatformFileError* error,
792     base::PlatformFileInfo* file_info,
793     base::FilePath* platform_path) {
794   // We're just returning the local file information.
795   *error = GetFileInfo(context, url, file_info, platform_path);
796   if (*error == base::PLATFORM_FILE_OK && file_info->is_directory) {
797     *file_info = base::PlatformFileInfo();
798     *error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
799   }
800   return webkit_blob::ScopedFile();
801 }
802
803 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
804     ObfuscatedFileUtil::CreateFileEnumerator(
805     FileSystemOperationContext* context,
806     const FileSystemURL& root_url,
807     bool recursive) {
808   SandboxDirectoryDatabase* db = GetDirectoryDatabase(root_url, false);
809   if (!db) {
810     return scoped_ptr<AbstractFileEnumerator>(new EmptyFileEnumerator());
811   }
812   return scoped_ptr<AbstractFileEnumerator>(
813       new ObfuscatedFileEnumerator(db, context, this, root_url, recursive));
814 }
815
816 bool ObfuscatedFileUtil::IsDirectoryEmpty(
817     FileSystemOperationContext* context,
818     const FileSystemURL& url) {
819   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
820   if (!db)
821     return true;  // Not a great answer, but it's what others do.
822   FileId file_id;
823   if (!db->GetFileWithPath(url.path(), &file_id))
824     return true;  // Ditto.
825   FileInfo file_info;
826   if (!db->GetFileInfo(file_id, &file_info)) {
827     DCHECK(!file_id);
828     // It's the root directory and the database hasn't been initialized yet.
829     return true;
830   }
831   if (!file_info.is_directory())
832     return true;
833   std::vector<FileId> children;
834   // TODO(ericu): This could easily be made faster with help from the database.
835   if (!db->ListChildren(file_id, &children))
836     return true;
837   return children.empty();
838 }
839
840 base::FilePath ObfuscatedFileUtil::GetDirectoryForOriginAndType(
841     const GURL& origin,
842     const std::string& type_string,
843     bool create,
844     base::PlatformFileError* error_code) {
845   base::FilePath origin_dir = GetDirectoryForOrigin(origin, create, error_code);
846   if (origin_dir.empty())
847     return base::FilePath();
848   if (type_string.empty())
849     return origin_dir;
850   base::FilePath path = origin_dir.AppendASCII(type_string);
851   base::PlatformFileError error = base::PLATFORM_FILE_OK;
852   if (!base::DirectoryExists(path) &&
853       (!create || !file_util::CreateDirectory(path))) {
854     error = create ?
855           base::PLATFORM_FILE_ERROR_FAILED :
856           base::PLATFORM_FILE_ERROR_NOT_FOUND;
857   }
858
859   if (error_code)
860     *error_code = error;
861   return path;
862 }
863
864 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
865     const GURL& origin,
866     const std::string& type_string) {
867   base::PlatformFileError error = base::PLATFORM_FILE_OK;
868   base::FilePath origin_type_path = GetDirectoryForOriginAndType(
869       origin, type_string, false, &error);
870   if (origin_type_path.empty())
871     return true;
872   if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
873     // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase.
874     // We ignore its error now since 1) it doesn't matter the final result, and
875     // 2) it always returns false in Windows because of LevelDB's
876     // implementation.
877     // Information about failure would be useful for debugging.
878     if (!type_string.empty())
879       DestroyDirectoryDatabase(origin, type_string);
880     if (!base::DeleteFile(origin_type_path, true /* recursive */))
881       return false;
882   }
883
884   base::FilePath origin_path = VirtualPath::DirName(origin_type_path);
885   DCHECK_EQ(origin_path.value(),
886             GetDirectoryForOrigin(origin, false, NULL).value());
887
888   if (!type_string.empty()) {
889     // At this point we are sure we had successfully deleted the origin/type
890     // directory (i.e. we're ready to just return true).
891     // See if we have other directories in this origin directory.
892     for (std::set<std::string>::iterator iter = known_type_strings_.begin();
893          iter != known_type_strings_.end();
894          ++iter) {
895       if (*iter == type_string)
896         continue;
897       if (base::DirectoryExists(origin_path.AppendASCII(*iter))) {
898         // Other type's directory exists; just return true here.
899         return true;
900       }
901     }
902   }
903
904   // No other directories seem exist. Try deleting the entire origin directory.
905   InitOriginDatabase(origin, false);
906   if (origin_database_) {
907     origin_database_->RemovePathForOrigin(
908         webkit_database::GetIdentifierFromOrigin(origin));
909   }
910   if (!base::DeleteFile(origin_path, true /* recursive */))
911     return false;
912
913   return true;
914 }
915
916 ObfuscatedFileUtil::AbstractOriginEnumerator*
917 ObfuscatedFileUtil::CreateOriginEnumerator() {
918   std::vector<SandboxOriginDatabase::OriginRecord> origins;
919
920   InitOriginDatabase(GURL(), false);
921   return new ObfuscatedOriginEnumerator(
922       origin_database_.get(), file_system_directory_);
923 }
924
925 bool ObfuscatedFileUtil::DestroyDirectoryDatabase(
926     const GURL& origin,
927     const std::string& type_string) {
928   std::string key = GetDirectoryDatabaseKey(origin, type_string);
929   if (key.empty())
930     return true;
931   DirectoryMap::iterator iter = directories_.find(key);
932   if (iter != directories_.end()) {
933     SandboxDirectoryDatabase* database = iter->second;
934     directories_.erase(iter);
935     delete database;
936   }
937
938   PlatformFileError error = base::PLATFORM_FILE_OK;
939   base::FilePath path = GetDirectoryForOriginAndType(
940       origin, type_string, false, &error);
941   if (path.empty() || error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
942     return true;
943   return SandboxDirectoryDatabase::DestroyDatabase(path);
944 }
945
946 // static
947 int64 ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath& path) {
948   return UsageForPath(VirtualPath::BaseName(path).value().size());
949 }
950
951 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
952     const std::vector<std::string>& type_strings_to_prepopulate) {
953   SandboxPrioritizedOriginDatabase database(file_system_directory_);
954   std::string origin_string = database.GetPrimaryOrigin();
955   if (origin_string.empty() || !database.HasOriginPath(origin_string))
956     return;
957   const GURL origin = webkit_database::GetOriginFromIdentifier(origin_string);
958
959   // Prepopulate the directory database(s) if and only if this instance
960   // has primary origin and the directory database is already there.
961   for (size_t i = 0; i < type_strings_to_prepopulate.size(); ++i) {
962     const std::string type_string = type_strings_to_prepopulate[i];
963     // Only handles known types.
964     if (!ContainsKey(known_type_strings_, type_string))
965       continue;
966     PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
967     base::FilePath path = GetDirectoryForOriginAndType(
968         origin, type_string, false, &error);
969     if (error != base::PLATFORM_FILE_OK)
970       continue;
971     scoped_ptr<SandboxDirectoryDatabase> db(new SandboxDirectoryDatabase(path));
972     if (db->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION)) {
973       directories_[GetDirectoryDatabaseKey(origin, type_string)] = db.release();
974       MarkUsed();
975       // Don't populate more than one database, as it may rather hurt
976       // performance.
977       break;
978     }
979   }
980 }
981
982 base::FilePath ObfuscatedFileUtil::GetDirectoryForURL(
983     const FileSystemURL& url,
984     bool create,
985     base::PlatformFileError* error_code) {
986   return GetDirectoryForOriginAndType(
987       url.origin(), CallGetTypeStringForURL(url), create, error_code);
988 }
989
990 std::string ObfuscatedFileUtil::CallGetTypeStringForURL(
991     const FileSystemURL& url) {
992   DCHECK(!get_type_string_for_url_.is_null());
993   return get_type_string_for_url_.Run(url);
994 }
995
996 PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal(
997     SandboxDirectoryDatabase* db,
998     FileSystemOperationContext* context,
999     const FileSystemURL& url,
1000     FileId file_id,
1001     FileInfo* local_info,
1002     base::PlatformFileInfo* file_info,
1003     base::FilePath* platform_file_path) {
1004   DCHECK(db);
1005   DCHECK(context);
1006   DCHECK(file_info);
1007   DCHECK(platform_file_path);
1008
1009   if (!db->GetFileInfo(file_id, local_info)) {
1010     NOTREACHED();
1011     return base::PLATFORM_FILE_ERROR_FAILED;
1012   }
1013
1014   if (local_info->is_directory()) {
1015     file_info->size = 0;
1016     file_info->is_directory = true;
1017     file_info->is_symbolic_link = false;
1018     file_info->last_modified = local_info->modification_time;
1019     *platform_file_path = base::FilePath();
1020     // We don't fill in ctime or atime.
1021     return base::PLATFORM_FILE_OK;
1022   }
1023   if (local_info->data_path.empty())
1024     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1025   base::FilePath local_path = DataPathToLocalPath(url, local_info->data_path);
1026   base::PlatformFileError error = NativeFileUtil::GetFileInfo(
1027       local_path, file_info);
1028   // We should not follow symbolic links in sandboxed file system.
1029   if (file_util::IsLink(local_path)) {
1030     LOG(WARNING) << "Found a symbolic file.";
1031     error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1032   }
1033   if (error == base::PLATFORM_FILE_OK) {
1034     *platform_file_path = local_path;
1035   } else if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
1036     LOG(WARNING) << "Lost a backing file.";
1037     InvalidateUsageCache(context, url.origin(), url.type());
1038     if (!db->RemoveFileInfo(file_id))
1039       return base::PLATFORM_FILE_ERROR_FAILED;
1040   }
1041   return error;
1042 }
1043
1044 PlatformFileError ObfuscatedFileUtil::CreateFile(
1045     FileSystemOperationContext* context,
1046     const base::FilePath& src_file_path,
1047     const FileSystemURL& dest_url,
1048     FileInfo* dest_file_info, int file_flags, PlatformFile* handle) {
1049   if (handle)
1050     *handle = base::kInvalidPlatformFileValue;
1051   SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true);
1052
1053   PlatformFileError error = base::PLATFORM_FILE_OK;
1054   base::FilePath root = GetDirectoryForURL(dest_url, false, &error);
1055   if (error != base::PLATFORM_FILE_OK)
1056     return error;
1057
1058   base::FilePath dest_local_path;
1059   error = GenerateNewLocalPath(db, context, dest_url, &dest_local_path);
1060   if (error != base::PLATFORM_FILE_OK)
1061     return error;
1062
1063   bool created = false;
1064   if (!src_file_path.empty()) {
1065     DCHECK(!file_flags);
1066     DCHECK(!handle);
1067     error = NativeFileUtil::CopyOrMoveFile(
1068         src_file_path, dest_local_path,
1069         FileSystemOperation::OPTION_NONE, true /* copy */);
1070     created = true;
1071   } else {
1072     if (base::PathExists(dest_local_path)) {
1073       if (!base::DeleteFile(dest_local_path, true /* recursive */)) {
1074         NOTREACHED();
1075         return base::PLATFORM_FILE_ERROR_FAILED;
1076       }
1077       LOG(WARNING) << "A stray file detected";
1078       InvalidateUsageCache(context, dest_url.origin(), dest_url.type());
1079     }
1080
1081     if (handle) {
1082       error = NativeFileUtil::CreateOrOpen(
1083           dest_local_path, file_flags, handle, &created);
1084       // If this succeeds, we must close handle on any subsequent error.
1085     } else {
1086       DCHECK(!file_flags);  // file_flags is only used by CreateOrOpen.
1087       error = NativeFileUtil::EnsureFileExists(dest_local_path, &created);
1088     }
1089   }
1090   if (error != base::PLATFORM_FILE_OK)
1091     return error;
1092
1093   if (!created) {
1094     NOTREACHED();
1095     if (handle) {
1096       DCHECK_NE(base::kInvalidPlatformFileValue, *handle);
1097       base::ClosePlatformFile(*handle);
1098       base::DeleteFile(dest_local_path, false /* recursive */);
1099       *handle = base::kInvalidPlatformFileValue;
1100     }
1101     return base::PLATFORM_FILE_ERROR_FAILED;
1102   }
1103
1104   // This removes the root, including the trailing slash, leaving a relative
1105   // path.
1106   dest_file_info->data_path = base::FilePath(
1107       dest_local_path.value().substr(root.value().length() + 1));
1108
1109   FileId file_id;
1110   error = db->AddFileInfo(*dest_file_info, &file_id);
1111   if (error != base::PLATFORM_FILE_OK) {
1112     if (handle) {
1113       DCHECK_NE(base::kInvalidPlatformFileValue, *handle);
1114       base::ClosePlatformFile(*handle);
1115       *handle = base::kInvalidPlatformFileValue;
1116     }
1117     base::DeleteFile(dest_local_path, false /* recursive */);
1118     return error;
1119   }
1120   TouchDirectory(db, dest_file_info->parent_id);
1121
1122   return base::PLATFORM_FILE_OK;
1123 }
1124
1125 base::FilePath ObfuscatedFileUtil::DataPathToLocalPath(
1126     const FileSystemURL& url, const base::FilePath& data_path) {
1127   PlatformFileError error = base::PLATFORM_FILE_OK;
1128   base::FilePath root = GetDirectoryForURL(url, false, &error);
1129   if (error != base::PLATFORM_FILE_OK)
1130     return base::FilePath();
1131   return root.Append(data_path);
1132 }
1133
1134 std::string ObfuscatedFileUtil::GetDirectoryDatabaseKey(
1135     const GURL& origin, const std::string& type_string) {
1136   if (type_string.empty()) {
1137     LOG(WARNING) << "Unknown filesystem type requested:" << type_string;
1138     return std::string();
1139   }
1140   // For isolated origin we just use a type string as a key.
1141   return webkit_database::GetIdentifierFromOrigin(origin) +
1142       type_string;
1143 }
1144
1145 // TODO(ericu): How to do the whole validation-without-creation thing?
1146 // We may not have quota even to create the database.
1147 // Ah, in that case don't even get here?
1148 // Still doesn't answer the quota issue, though.
1149 SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase(
1150     const FileSystemURL& url, bool create) {
1151   std::string key = GetDirectoryDatabaseKey(
1152       url.origin(), CallGetTypeStringForURL(url));
1153   if (key.empty())
1154     return NULL;
1155
1156   DirectoryMap::iterator iter = directories_.find(key);
1157   if (iter != directories_.end()) {
1158     MarkUsed();
1159     return iter->second;
1160   }
1161
1162   PlatformFileError error = base::PLATFORM_FILE_OK;
1163   base::FilePath path = GetDirectoryForURL(url, create, &error);
1164   if (error != base::PLATFORM_FILE_OK) {
1165     LOG(WARNING) << "Failed to get origin+type directory: " << path.value();
1166     return NULL;
1167   }
1168   MarkUsed();
1169   SandboxDirectoryDatabase* database = new SandboxDirectoryDatabase(path);
1170   directories_[key] = database;
1171   return database;
1172 }
1173
1174 base::FilePath ObfuscatedFileUtil::GetDirectoryForOrigin(
1175     const GURL& origin, bool create, base::PlatformFileError* error_code) {
1176   if (!InitOriginDatabase(origin, create)) {
1177     if (error_code) {
1178       *error_code = create ?
1179           base::PLATFORM_FILE_ERROR_FAILED :
1180           base::PLATFORM_FILE_ERROR_NOT_FOUND;
1181     }
1182     return base::FilePath();
1183   }
1184   base::FilePath directory_name;
1185   std::string id = webkit_database::GetIdentifierFromOrigin(origin);
1186
1187   bool exists_in_db = origin_database_->HasOriginPath(id);
1188   if (!exists_in_db && !create) {
1189     if (error_code)
1190       *error_code = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1191     return base::FilePath();
1192   }
1193   if (!origin_database_->GetPathForOrigin(id, &directory_name)) {
1194     if (error_code)
1195       *error_code = base::PLATFORM_FILE_ERROR_FAILED;
1196     return base::FilePath();
1197   }
1198
1199   base::FilePath path = file_system_directory_.Append(directory_name);
1200   bool exists_in_fs = base::DirectoryExists(path);
1201   if (!exists_in_db && exists_in_fs) {
1202     if (!base::DeleteFile(path, true)) {
1203       if (error_code)
1204         *error_code = base::PLATFORM_FILE_ERROR_FAILED;
1205       return base::FilePath();
1206     }
1207     exists_in_fs = false;
1208   }
1209
1210   if (!exists_in_fs) {
1211     if (!create || !file_util::CreateDirectory(path)) {
1212       if (error_code)
1213         *error_code = create ?
1214             base::PLATFORM_FILE_ERROR_FAILED :
1215             base::PLATFORM_FILE_ERROR_NOT_FOUND;
1216       return base::FilePath();
1217     }
1218   }
1219
1220   if (error_code)
1221     *error_code = base::PLATFORM_FILE_OK;
1222
1223   return path;
1224 }
1225
1226 void ObfuscatedFileUtil::InvalidateUsageCache(
1227     FileSystemOperationContext* context,
1228     const GURL& origin,
1229     FileSystemType type) {
1230   if (sandbox_delegate_)
1231     sandbox_delegate_->InvalidateUsageCache(origin, type);
1232 }
1233
1234 void ObfuscatedFileUtil::MarkUsed() {
1235   if (!timer_)
1236     timer_.reset(new TimedTaskHelper(file_task_runner_.get()));
1237
1238   if (timer_->IsRunning()) {
1239     timer_->Reset();
1240   } else {
1241     timer_->Start(FROM_HERE,
1242                   base::TimeDelta::FromSeconds(db_flush_delay_seconds_),
1243                   base::Bind(&ObfuscatedFileUtil::DropDatabases,
1244                              base::Unretained(this)));
1245   }
1246 }
1247
1248 void ObfuscatedFileUtil::DropDatabases() {
1249   origin_database_.reset();
1250   STLDeleteContainerPairSecondPointers(
1251       directories_.begin(), directories_.end());
1252   directories_.clear();
1253   timer_.reset();
1254 }
1255
1256 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL& origin_hint,
1257                                             bool create) {
1258   if (origin_database_)
1259     return true;
1260
1261   if (!create && !base::DirectoryExists(file_system_directory_))
1262     return false;
1263   if (!file_util::CreateDirectory(file_system_directory_)) {
1264     LOG(WARNING) << "Failed to create FileSystem directory: " <<
1265         file_system_directory_.value();
1266     return false;
1267   }
1268
1269   SandboxPrioritizedOriginDatabase* prioritized_origin_database =
1270       new SandboxPrioritizedOriginDatabase(file_system_directory_);
1271   origin_database_.reset(prioritized_origin_database);
1272
1273   if (origin_hint.is_empty() || !HasIsolatedStorage(origin_hint))
1274     return true;
1275
1276   const std::string isolated_origin_string =
1277       webkit_database::GetIdentifierFromOrigin(origin_hint);
1278
1279   // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
1280   base::FilePath isolated_origin_dir = file_system_directory_.Append(
1281       SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory);
1282   if (base::DirectoryExists(isolated_origin_dir) &&
1283       prioritized_origin_database->GetSandboxOriginDatabase()) {
1284     SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
1285         isolated_origin_string,
1286         file_system_directory_,
1287         prioritized_origin_database->GetSandboxOriginDatabase());
1288   }
1289
1290   prioritized_origin_database->InitializePrimaryOrigin(
1291       isolated_origin_string);
1292
1293   return true;
1294 }
1295
1296 PlatformFileError ObfuscatedFileUtil::GenerateNewLocalPath(
1297     SandboxDirectoryDatabase* db,
1298     FileSystemOperationContext* context,
1299     const FileSystemURL& url,
1300     base::FilePath* local_path) {
1301   DCHECK(local_path);
1302   int64 number;
1303   if (!db || !db->GetNextInteger(&number))
1304     return base::PLATFORM_FILE_ERROR_FAILED;
1305
1306   PlatformFileError error = base::PLATFORM_FILE_OK;
1307   base::FilePath new_local_path = GetDirectoryForURL(url, false, &error);
1308   if (error != base::PLATFORM_FILE_OK)
1309     return base::PLATFORM_FILE_ERROR_FAILED;
1310
1311   // We use the third- and fourth-to-last digits as the directory.
1312   int64 directory_number = number % 10000 / 100;
1313   new_local_path = new_local_path.AppendASCII(
1314       base::StringPrintf("%02" PRId64, directory_number));
1315
1316   error = NativeFileUtil::CreateDirectory(
1317       new_local_path, false /* exclusive */, false /* recursive */);
1318   if (error != base::PLATFORM_FILE_OK)
1319     return error;
1320
1321   *local_path =
1322       new_local_path.AppendASCII(base::StringPrintf("%08" PRId64, number));
1323   return base::PLATFORM_FILE_OK;
1324 }
1325
1326 PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal(
1327     FileSystemOperationContext* context,
1328     const FileSystemURL& url, int file_flags,
1329     PlatformFile* file_handle, bool* created) {
1330   DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE |
1331         base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ |
1332         base::PLATFORM_FILE_EXCLUSIVE_WRITE)));
1333   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
1334   if (!db)
1335     return base::PLATFORM_FILE_ERROR_FAILED;
1336   FileId file_id;
1337   if (!db->GetFileWithPath(url.path(), &file_id)) {
1338     // The file doesn't exist.
1339     if (!(file_flags & (base::PLATFORM_FILE_CREATE |
1340         base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_ALWAYS)))
1341       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
1342     FileId parent_id;
1343     if (!db->GetFileWithPath(VirtualPath::DirName(url.path()),
1344                              &parent_id))
1345       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
1346     FileInfo file_info;
1347     InitFileInfo(&file_info, parent_id,
1348                  VirtualPath::BaseName(url.path()).value());
1349
1350     int64 growth = UsageForPath(file_info.name.size());
1351     if (!AllocateQuota(context, growth))
1352       return base::PLATFORM_FILE_ERROR_NO_SPACE;
1353     PlatformFileError error = CreateFile(
1354         context, base::FilePath(),
1355         url, &file_info, file_flags, file_handle);
1356     if (created && base::PLATFORM_FILE_OK == error) {
1357       *created = true;
1358       UpdateUsage(context, url, growth);
1359       context->change_observers()->Notify(
1360           &FileChangeObserver::OnCreateFile, MakeTuple(url));
1361     }
1362     return error;
1363   }
1364
1365   if (file_flags & base::PLATFORM_FILE_CREATE)
1366     return base::PLATFORM_FILE_ERROR_EXISTS;
1367
1368   base::PlatformFileInfo platform_file_info;
1369   base::FilePath local_path;
1370   FileInfo file_info;
1371   base::PlatformFileError error = GetFileInfoInternal(
1372       db, context, url, file_id, &file_info, &platform_file_info, &local_path);
1373   if (error != base::PLATFORM_FILE_OK)
1374     return error;
1375   if (file_info.is_directory())
1376     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
1377
1378   int64 delta = 0;
1379   if (file_flags & (base::PLATFORM_FILE_CREATE_ALWAYS |
1380                     base::PLATFORM_FILE_OPEN_TRUNCATED)) {
1381     // The file exists and we're truncating.
1382     delta = -platform_file_info.size;
1383     AllocateQuota(context, delta);
1384   }
1385
1386   error = NativeFileUtil::CreateOrOpen(
1387       local_path, file_flags, file_handle, created);
1388   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
1389     // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
1390     // TODO(tzik): Delete database entry after ensuring the file lost.
1391     InvalidateUsageCache(context, url.origin(), url.type());
1392     LOG(WARNING) << "Lost a backing file.";
1393     error = base::PLATFORM_FILE_ERROR_FAILED;
1394   }
1395
1396   // If truncating we need to update the usage.
1397   if (error == base::PLATFORM_FILE_OK && delta) {
1398     UpdateUsage(context, url, delta);
1399     context->change_observers()->Notify(
1400         &FileChangeObserver::OnModifyFile, MakeTuple(url));
1401   }
1402   return error;
1403 }
1404
1405 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL& origin) {
1406   return special_storage_policy_.get() &&
1407       special_storage_policy_->HasIsolatedStorage(origin);
1408 }
1409
1410 }  // namespace fileapi