Home | History | Annotate | Download | only in drive
      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 "chrome/browser/chromeos/drive/resource_metadata.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "base/sys_info.h"
     10 #include "chrome/browser/chromeos/drive/drive.pb.h"
     11 #include "chrome/browser/chromeos/drive/file_system_util.h"
     12 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
     13 #include "content/public/browser/browser_thread.h"
     14 
     15 using content::BrowserThread;
     16 
     17 namespace drive {
     18 namespace {
     19 
     20 // Sets entry's base name from its title and other attributes.
     21 void SetBaseNameFromTitle(ResourceEntry* entry) {
     22   std::string base_name = entry->title();
     23   if (entry->has_file_specific_info() &&
     24       entry->file_specific_info().is_hosted_document()) {
     25     base_name += entry->file_specific_info().document_extension();
     26   }
     27   entry->set_base_name(util::NormalizeFileName(base_name));
     28 }
     29 
     30 // Creates an entry by copying |source|, and setting the base name properly.
     31 ResourceEntry CreateEntryWithProperBaseName(const ResourceEntry& source) {
     32   ResourceEntry entry(source);
     33   SetBaseNameFromTitle(&entry);
     34   return entry;
     35 }
     36 
     37 // Returns true if enough disk space is available for DB operation.
     38 // TODO(hashimoto): Merge this with FileCache's FreeDiskSpaceGetterInterface.
     39 bool EnoughDiskSpaceIsAvailableForDBOperation(const base::FilePath& path) {
     40   const int64 kRequiredDiskSpaceInMB = 128;  // 128 MB seems to be large enough.
     41   return base::SysInfo::AmountOfFreeDiskSpace(path) >=
     42       kRequiredDiskSpaceInMB * (1 << 20);
     43 }
     44 
     45 // Runs |callback| with arguments.
     46 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
     47                                  scoped_ptr<ResourceEntry> entry,
     48                                  FileError error) {
     49   DCHECK(!callback.is_null());
     50 
     51   if (error != FILE_ERROR_OK)
     52     entry.reset();
     53   callback.Run(error, entry.Pass());
     54 }
     55 
     56 // Runs |callback| with arguments.
     57 void RunReadDirectoryCallback(const ReadDirectoryCallback& callback,
     58                               scoped_ptr<ResourceEntryVector> entries,
     59                               FileError error) {
     60   DCHECK(!callback.is_null());
     61 
     62   if (error != FILE_ERROR_OK)
     63     entries.reset();
     64   callback.Run(error, entries.Pass());
     65 }
     66 
     67 // Runs |callback| with arguments.
     68 void RunFileMoveCallback(const FileMoveCallback& callback,
     69                          base::FilePath* path,
     70                          FileError error) {
     71   DCHECK(!callback.is_null());
     72   DCHECK(path);
     73 
     74   callback.Run(error, *path);
     75 }
     76 
     77 // Helper function to run tasks with FileMoveCallback.
     78 void PostFileMoveTask(
     79     base::TaskRunner* task_runner,
     80     const base::Callback<FileError(base::FilePath* out_file_path)>& task,
     81     const FileMoveCallback& callback) {
     82   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     83   DCHECK(!task.is_null());
     84   DCHECK(!callback.is_null());
     85 
     86   base::FilePath* file_path = new base::FilePath;
     87 
     88   base::PostTaskAndReplyWithResult(
     89       task_runner,
     90       FROM_HERE,
     91       base::Bind(task, file_path),
     92       base::Bind(&RunFileMoveCallback, callback, base::Owned(file_path)));
     93 }
     94 
     95 FileError AddEntryWithFilePath(internal::ResourceMetadata* metadata,
     96                                const ResourceEntry& entry,
     97                                base::FilePath* out_file_path) {
     98   DCHECK(metadata);
     99   DCHECK(out_file_path);
    100   FileError error = metadata->AddEntry(entry);
    101   if (error == FILE_ERROR_OK)
    102     *out_file_path = metadata->GetFilePath(entry.resource_id());
    103   return error;
    104 }
    105 
    106 }  // namespace
    107 
    108 std::string DirectoryFetchInfo::ToString() const {
    109   return ("resource_id: " + resource_id_ +
    110           ", changestamp: " + base::Int64ToString(changestamp_));
    111 }
    112 
    113 EntryInfoResult::EntryInfoResult() : error(FILE_ERROR_FAILED) {
    114 }
    115 
    116 EntryInfoResult::~EntryInfoResult() {
    117 }
    118 
    119 EntryInfoPairResult::EntryInfoPairResult() {
    120 }
    121 
    122 EntryInfoPairResult::~EntryInfoPairResult() {
    123 }
    124 
    125 namespace internal {
    126 
    127 ResourceMetadata::ResourceMetadata(
    128     ResourceMetadataStorage* storage,
    129     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
    130     : blocking_task_runner_(blocking_task_runner),
    131       storage_(storage),
    132       weak_ptr_factory_(this) {
    133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    134 }
    135 
    136 FileError ResourceMetadata::Initialize() {
    137   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    138 
    139   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    140     return FILE_ERROR_NO_LOCAL_SPACE;
    141 
    142   if (!SetUpDefaultEntries())
    143     return FILE_ERROR_FAILED;
    144 
    145   return FILE_ERROR_OK;
    146 }
    147 
    148 void ResourceMetadata::Destroy() {
    149   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    150 
    151   weak_ptr_factory_.InvalidateWeakPtrs();
    152   blocking_task_runner_->PostTask(
    153       FROM_HERE,
    154       base::Bind(&ResourceMetadata::DestroyOnBlockingPool,
    155                  base::Unretained(this)));
    156 }
    157 
    158 void ResourceMetadata::ResetOnUIThread(const FileOperationCallback& callback) {
    159   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    160   DCHECK(!callback.is_null());
    161 
    162   base::PostTaskAndReplyWithResult(
    163       blocking_task_runner_.get(),
    164       FROM_HERE,
    165       base::Bind(&ResourceMetadata::Reset, base::Unretained(this)),
    166       callback);
    167 }
    168 
    169 FileError ResourceMetadata::Reset() {
    170   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    171 
    172   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    173     return FILE_ERROR_NO_LOCAL_SPACE;
    174 
    175   if (!storage_->SetLargestChangestamp(0) ||
    176       !RemoveEntryRecursively(util::kDriveGrandRootSpecialResourceId) ||
    177       !SetUpDefaultEntries())
    178     return FILE_ERROR_FAILED;
    179 
    180   return FILE_ERROR_OK;
    181 }
    182 
    183 ResourceMetadata::~ResourceMetadata() {
    184   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    185 }
    186 
    187 bool ResourceMetadata::SetUpDefaultEntries() {
    188   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    189 
    190   // Initialize the grand root and "other" entries. "/drive" and "/drive/other".
    191   // As an intermediate change, "/drive/root" is also added here.
    192   ResourceEntry entry;
    193   if (!storage_->GetEntry(util::kDriveGrandRootSpecialResourceId, &entry)) {
    194     ResourceEntry root;
    195     root.mutable_file_info()->set_is_directory(true);
    196     root.set_resource_id(util::kDriveGrandRootSpecialResourceId);
    197     root.set_title(util::kDriveGrandRootDirName);
    198     if (!storage_->PutEntry(CreateEntryWithProperBaseName(root)))
    199       return false;
    200   }
    201   if (!storage_->GetEntry(util::kDriveOtherDirSpecialResourceId, &entry)) {
    202     if (!PutEntryUnderDirectory(util::CreateOtherDirEntry()))
    203       return false;
    204   }
    205   return true;
    206 }
    207 
    208 void ResourceMetadata::DestroyOnBlockingPool() {
    209   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    210   delete this;
    211 }
    212 
    213 void ResourceMetadata::GetLargestChangestampOnUIThread(
    214     const GetChangestampCallback& callback) {
    215   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    216   DCHECK(!callback.is_null());
    217   base::PostTaskAndReplyWithResult(
    218       blocking_task_runner_.get(),
    219       FROM_HERE,
    220       base::Bind(&ResourceMetadata::GetLargestChangestamp,
    221                  base::Unretained(this)),
    222       callback);
    223 }
    224 
    225 void ResourceMetadata::SetLargestChangestampOnUIThread(
    226     int64 value,
    227     const FileOperationCallback& callback) {
    228   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    229   DCHECK(!callback.is_null());
    230   base::PostTaskAndReplyWithResult(
    231       blocking_task_runner_.get(),
    232       FROM_HERE,
    233       base::Bind(&ResourceMetadata::SetLargestChangestamp,
    234                  base::Unretained(this),
    235                  value),
    236       callback);
    237 }
    238 
    239 int64 ResourceMetadata::GetLargestChangestamp() {
    240   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    241   return storage_->GetLargestChangestamp();
    242 }
    243 
    244 FileError ResourceMetadata::SetLargestChangestamp(int64 value) {
    245   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    246 
    247   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    248     return FILE_ERROR_NO_LOCAL_SPACE;
    249 
    250   storage_->SetLargestChangestamp(value);
    251   return FILE_ERROR_OK;
    252 }
    253 
    254 void ResourceMetadata::AddEntryOnUIThread(const ResourceEntry& entry,
    255                                           const FileMoveCallback& callback) {
    256   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    257   DCHECK(!callback.is_null());
    258 
    259   PostFileMoveTask(
    260       blocking_task_runner_.get(),
    261       base::Bind(&AddEntryWithFilePath, base::Unretained(this), entry),
    262       callback);
    263 }
    264 
    265 FileError ResourceMetadata::AddEntry(const ResourceEntry& entry) {
    266   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    267 
    268   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    269     return FILE_ERROR_NO_LOCAL_SPACE;
    270 
    271   ResourceEntry existing_entry;
    272   if (storage_->GetEntry(entry.resource_id(), &existing_entry))
    273     return FILE_ERROR_EXISTS;
    274 
    275   ResourceEntry parent;
    276   if (!storage_->GetEntry(entry.parent_resource_id(), &parent) ||
    277       !parent.file_info().is_directory())
    278     return FILE_ERROR_NOT_FOUND;
    279 
    280   if (!PutEntryUnderDirectory(entry))
    281     return FILE_ERROR_FAILED;
    282 
    283   return FILE_ERROR_OK;
    284 }
    285 
    286 void ResourceMetadata::MoveEntryToDirectoryOnUIThread(
    287     const base::FilePath& file_path,
    288     const base::FilePath& directory_path,
    289     const FileMoveCallback& callback) {
    290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    291   DCHECK(!callback.is_null());
    292 
    293   PostFileMoveTask(blocking_task_runner_.get(),
    294                    base::Bind(&ResourceMetadata::MoveEntryToDirectory,
    295                               base::Unretained(this),
    296                               file_path,
    297                               directory_path),
    298                    callback);
    299 }
    300 
    301 void ResourceMetadata::RenameEntryOnUIThread(const base::FilePath& file_path,
    302                                              const std::string& new_name,
    303                                              const FileMoveCallback& callback) {
    304   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    305   DCHECK(!callback.is_null());
    306 
    307   PostFileMoveTask(blocking_task_runner_.get(),
    308                    base::Bind(&ResourceMetadata::RenameEntry,
    309                               base::Unretained(this),
    310                               file_path,
    311                               new_name),
    312                    callback);
    313 }
    314 
    315 FileError ResourceMetadata::RemoveEntry(const std::string& resource_id) {
    316   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    317 
    318   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    319     return FILE_ERROR_NO_LOCAL_SPACE;
    320 
    321   // Disallow deletion of special entries "/drive" and "/drive/other".
    322   if (util::IsSpecialResourceId(resource_id))
    323     return FILE_ERROR_ACCESS_DENIED;
    324 
    325   ResourceEntry entry;
    326   if (!storage_->GetEntry(resource_id, &entry))
    327     return FILE_ERROR_NOT_FOUND;
    328 
    329   if (!RemoveEntryRecursively(entry.resource_id()))
    330     return FILE_ERROR_FAILED;
    331   return FILE_ERROR_OK;
    332 }
    333 
    334 void ResourceMetadata::GetResourceEntryByIdOnUIThread(
    335     const std::string& resource_id,
    336     const GetResourceEntryCallback& callback) {
    337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    338   DCHECK(!callback.is_null());
    339 
    340   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
    341   ResourceEntry* entry_ptr = entry.get();
    342   base::PostTaskAndReplyWithResult(
    343       blocking_task_runner_.get(),
    344       FROM_HERE,
    345       base::Bind(&ResourceMetadata::GetResourceEntryById,
    346                  base::Unretained(this),
    347                  resource_id,
    348                  entry_ptr),
    349       base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry)));
    350 }
    351 
    352 FileError ResourceMetadata::GetResourceEntryById(
    353     const std::string& resource_id,
    354     ResourceEntry* out_entry) {
    355   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    356   DCHECK(!resource_id.empty());
    357   DCHECK(out_entry);
    358 
    359   return storage_->GetEntry(resource_id, out_entry) ?
    360       FILE_ERROR_OK : FILE_ERROR_NOT_FOUND;
    361 }
    362 
    363 void ResourceMetadata::GetResourceEntryByPathOnUIThread(
    364     const base::FilePath& file_path,
    365     const GetResourceEntryCallback& callback) {
    366   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    367   DCHECK(!callback.is_null());
    368 
    369   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
    370   ResourceEntry* entry_ptr = entry.get();
    371   base::PostTaskAndReplyWithResult(
    372       blocking_task_runner_.get(),
    373       FROM_HERE,
    374       base::Bind(&ResourceMetadata::GetResourceEntryByPath,
    375                  base::Unretained(this),
    376                  file_path,
    377                  entry_ptr),
    378       base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry)));
    379 }
    380 
    381 FileError ResourceMetadata::GetResourceEntryByPath(const base::FilePath& path,
    382                                                    ResourceEntry* out_entry) {
    383   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    384   DCHECK(out_entry);
    385 
    386   return FindEntryByPathSync(path, out_entry) ?
    387       FILE_ERROR_OK : FILE_ERROR_NOT_FOUND;
    388 }
    389 
    390 void ResourceMetadata::ReadDirectoryByPathOnUIThread(
    391     const base::FilePath& file_path,
    392     const ReadDirectoryCallback& callback) {
    393   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    394   DCHECK(!callback.is_null());
    395 
    396   scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector);
    397   ResourceEntryVector* entries_ptr = entries.get();
    398   base::PostTaskAndReplyWithResult(
    399       blocking_task_runner_.get(),
    400       FROM_HERE,
    401       base::Bind(&ResourceMetadata::ReadDirectoryByPath,
    402                  base::Unretained(this),
    403                  file_path,
    404                  entries_ptr),
    405       base::Bind(&RunReadDirectoryCallback, callback, base::Passed(&entries)));
    406 }
    407 
    408 FileError ResourceMetadata::ReadDirectoryByPath(
    409     const base::FilePath& path,
    410     ResourceEntryVector* out_entries) {
    411   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    412   DCHECK(out_entries);
    413 
    414   ResourceEntry entry;
    415   if (!FindEntryByPathSync(path, &entry))
    416     return FILE_ERROR_NOT_FOUND;
    417 
    418   if (!entry.file_info().is_directory())
    419     return FILE_ERROR_NOT_A_DIRECTORY;
    420 
    421   std::vector<std::string> children;
    422   storage_->GetChildren(entry.resource_id(), &children);
    423 
    424   ResourceEntryVector entries(children.size());
    425   for (size_t i = 0; i < children.size(); ++i) {
    426     if (!storage_->GetEntry(children[i], &entries[i]))
    427       return FILE_ERROR_FAILED;
    428   }
    429   out_entries->swap(entries);
    430   return FILE_ERROR_OK;
    431 }
    432 
    433 FileError ResourceMetadata::RefreshEntry(const ResourceEntry& entry) {
    434   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    435 
    436   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    437     return FILE_ERROR_NO_LOCAL_SPACE;
    438 
    439   ResourceEntry old_entry;
    440   if (!storage_->GetEntry(entry.resource_id(), &old_entry))
    441     return FILE_ERROR_NOT_FOUND;
    442 
    443   if (old_entry.parent_resource_id().empty() ||  // Reject root.
    444       old_entry.file_info().is_directory() !=  // Reject incompatible input.
    445       entry.file_info().is_directory())
    446     return FILE_ERROR_INVALID_OPERATION;
    447 
    448   // Update data.
    449   ResourceEntry new_parent;
    450   if (!storage_->GetEntry(entry.parent_resource_id(), &new_parent) ||
    451       !new_parent.file_info().is_directory())
    452     return FILE_ERROR_NOT_FOUND;
    453 
    454   // Remove from the old parent and add it to the new parent with the new data.
    455   if (!PutEntryUnderDirectory(CreateEntryWithProperBaseName(entry)))
    456     return FILE_ERROR_FAILED;
    457   return FILE_ERROR_OK;
    458 }
    459 
    460 void ResourceMetadata::RefreshDirectoryOnUIThread(
    461     const DirectoryFetchInfo& directory_fetch_info,
    462     const ResourceEntryMap& entry_map,
    463     const FileMoveCallback& callback) {
    464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    465   DCHECK(!callback.is_null());
    466 
    467   PostFileMoveTask(blocking_task_runner_.get(),
    468                    base::Bind(&ResourceMetadata::RefreshDirectory,
    469                               base::Unretained(this),
    470                               directory_fetch_info,
    471                               entry_map),
    472                    callback);
    473 }
    474 
    475 void ResourceMetadata::GetChildDirectories(
    476     const std::string& resource_id,
    477     std::set<base::FilePath>* child_directories) {
    478   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    479 
    480   std::vector<std::string> children;
    481   storage_->GetChildren(resource_id, &children);
    482   for (size_t i = 0; i < children.size(); ++i) {
    483     ResourceEntry entry;
    484     if (storage_->GetEntry(children[i], &entry) &&
    485         entry.file_info().is_directory()) {
    486       child_directories->insert(GetFilePath(entry.resource_id()));
    487       GetChildDirectories(entry.resource_id(), child_directories);
    488     }
    489   }
    490 }
    491 
    492 std::string ResourceMetadata::GetChildResourceId(
    493     const std::string& parent_resource_id, const std::string& base_name) {
    494   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    495   return storage_->GetChild(parent_resource_id, base_name);
    496 }
    497 
    498 scoped_ptr<ResourceMetadata::Iterator> ResourceMetadata::GetIterator() {
    499   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    500 
    501   return storage_->GetIterator();
    502 }
    503 
    504 base::FilePath ResourceMetadata::GetFilePath(
    505     const std::string& resource_id) {
    506   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    507 
    508   base::FilePath path;
    509   ResourceEntry entry;
    510   if (storage_->GetEntry(resource_id, &entry)) {
    511     if (!entry.parent_resource_id().empty())
    512       path = GetFilePath(entry.parent_resource_id());
    513     path = path.Append(base::FilePath::FromUTF8Unsafe(entry.base_name()));
    514   }
    515   return path;
    516 }
    517 
    518 FileError ResourceMetadata::MoveEntryToDirectory(
    519     const base::FilePath& file_path,
    520     const base::FilePath& directory_path,
    521     base::FilePath* out_file_path) {
    522   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    523   DCHECK(!directory_path.empty());
    524   DCHECK(!file_path.empty());
    525   DCHECK(out_file_path);
    526 
    527   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    528     return FILE_ERROR_NO_LOCAL_SPACE;
    529 
    530   ResourceEntry entry, destination;
    531   if (!FindEntryByPathSync(file_path, &entry) ||
    532       !FindEntryByPathSync(directory_path, &destination))
    533     return FILE_ERROR_NOT_FOUND;
    534   if (!destination.file_info().is_directory())
    535     return FILE_ERROR_NOT_A_DIRECTORY;
    536 
    537   entry.set_parent_resource_id(destination.resource_id());
    538 
    539   FileError error = RefreshEntry(entry);
    540   if (error == FILE_ERROR_OK)
    541     *out_file_path = GetFilePath(entry.resource_id());
    542   return error;
    543 }
    544 
    545 FileError ResourceMetadata::RenameEntry(
    546     const base::FilePath& file_path,
    547     const std::string& new_title,
    548     base::FilePath* out_file_path) {
    549   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    550   DCHECK(!file_path.empty());
    551   DCHECK(!new_title.empty());
    552   DCHECK(out_file_path);
    553 
    554   DVLOG(1) << "RenameEntry " << file_path.value() << " to " << new_title;
    555 
    556   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    557     return FILE_ERROR_NO_LOCAL_SPACE;
    558 
    559   ResourceEntry entry;
    560   if (!FindEntryByPathSync(file_path, &entry))
    561     return FILE_ERROR_NOT_FOUND;
    562 
    563   if (base::FilePath::FromUTF8Unsafe(new_title) == file_path.BaseName())
    564     return FILE_ERROR_EXISTS;
    565 
    566   entry.set_title(new_title);
    567 
    568   FileError error = RefreshEntry(entry);
    569   if (error == FILE_ERROR_OK)
    570     *out_file_path = GetFilePath(entry.resource_id());
    571   return error;
    572 }
    573 
    574 bool ResourceMetadata::FindEntryByPathSync(const base::FilePath& file_path,
    575                                            ResourceEntry* out_entry) {
    576   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    577 
    578   // Start from the root.
    579   ResourceEntry entry;
    580   if (!storage_->GetEntry(util::kDriveGrandRootSpecialResourceId, &entry))
    581     return false;
    582   DCHECK(entry.parent_resource_id().empty());
    583 
    584   // Check the first component.
    585   std::vector<base::FilePath::StringType> components;
    586   file_path.GetComponents(&components);
    587   if (components.empty() ||
    588       base::FilePath(components[0]).AsUTF8Unsafe() != entry.base_name())
    589     return scoped_ptr<ResourceEntry>();
    590 
    591   // Iterate over the remaining components.
    592   for (size_t i = 1; i < components.size(); ++i) {
    593     const std::string component = base::FilePath(components[i]).AsUTF8Unsafe();
    594     const std::string resource_id = storage_->GetChild(entry.resource_id(),
    595                                                        component);
    596     if (resource_id.empty() || !storage_->GetEntry(resource_id, &entry))
    597       return false;
    598     DCHECK_EQ(entry.base_name(), component);
    599   }
    600   out_entry->Swap(&entry);
    601   return true;
    602 }
    603 
    604 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThread(
    605     const base::FilePath& first_path,
    606     const base::FilePath& second_path,
    607     const GetResourceEntryPairCallback& callback) {
    608   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    609   DCHECK(!callback.is_null());
    610 
    611   // Get the first entry.
    612   GetResourceEntryByPathOnUIThread(
    613       first_path,
    614       base::Bind(
    615           &ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetFirst,
    616           weak_ptr_factory_.GetWeakPtr(),
    617           first_path,
    618           second_path,
    619           callback));
    620 }
    621 
    622 FileError ResourceMetadata::RefreshDirectory(
    623     const DirectoryFetchInfo& directory_fetch_info,
    624     const ResourceEntryMap& entry_map,
    625     base::FilePath* out_file_path) {
    626   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    627   DCHECK(!directory_fetch_info.empty());
    628 
    629   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    630     return FILE_ERROR_NO_LOCAL_SPACE;
    631 
    632   ResourceEntry directory;
    633   if (!storage_->GetEntry(directory_fetch_info.resource_id(), &directory))
    634     return FILE_ERROR_NOT_FOUND;
    635 
    636   if (!directory.file_info().is_directory())
    637     return FILE_ERROR_NOT_A_DIRECTORY;
    638 
    639   directory.mutable_directory_specific_info()->set_changestamp(
    640       directory_fetch_info.changestamp());
    641   storage_->PutEntry(directory);
    642 
    643   // Go through the entry map. Handle existing entries and new entries.
    644   for (ResourceEntryMap::const_iterator it = entry_map.begin();
    645        it != entry_map.end(); ++it) {
    646     if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
    647       return FILE_ERROR_NO_LOCAL_SPACE;
    648 
    649     const ResourceEntry& entry = it->second;
    650     // Skip if the parent resource ID does not match. This is needed to
    651     // handle entries with multiple parents. For such entries, the first
    652     // parent is picked and other parents are ignored, hence some entries may
    653     // have a parent resource ID which does not match the target directory's.
    654     //
    655     // TODO(satorux): Move the filtering logic to somewhere more appropriate.
    656     // crbug.com/193525.
    657     if (entry.parent_resource_id() !=
    658         directory_fetch_info.resource_id()) {
    659       DVLOG(1) << "Wrong-parent entry rejected: " << entry.resource_id();
    660       continue;
    661     }
    662 
    663     if (!PutEntryUnderDirectory(CreateEntryWithProperBaseName(entry)))
    664       return FILE_ERROR_FAILED;
    665   }
    666 
    667   if (out_file_path)
    668     *out_file_path = GetFilePath(directory.resource_id());
    669 
    670   return FILE_ERROR_OK;
    671 }
    672 
    673 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetFirst(
    674     const base::FilePath& first_path,
    675     const base::FilePath& second_path,
    676     const GetResourceEntryPairCallback& callback,
    677     FileError error,
    678     scoped_ptr<ResourceEntry> entry) {
    679   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    680   DCHECK(!callback.is_null());
    681 
    682   scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult);
    683   result->first.path = first_path;
    684   result->first.error = error;
    685   result->first.entry = entry.Pass();
    686 
    687   // If the first one is not found, don't continue.
    688   if (error != FILE_ERROR_OK) {
    689     callback.Run(result.Pass());
    690     return;
    691   }
    692 
    693   // Get the second entry.
    694   GetResourceEntryByPathOnUIThread(
    695       second_path,
    696       base::Bind(
    697           &ResourceMetadata::
    698           GetResourceEntryPairByPathsOnUIThreadAfterGetSecond,
    699           weak_ptr_factory_.GetWeakPtr(),
    700           second_path,
    701           callback,
    702           base::Passed(&result)));
    703 }
    704 
    705 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetSecond(
    706     const base::FilePath& second_path,
    707     const GetResourceEntryPairCallback& callback,
    708     scoped_ptr<EntryInfoPairResult> result,
    709     FileError error,
    710     scoped_ptr<ResourceEntry> entry) {
    711   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    712   DCHECK(!callback.is_null());
    713   DCHECK(result.get());
    714 
    715   result->second.path = second_path;
    716   result->second.error = error;
    717   result->second.entry = entry.Pass();
    718 
    719   callback.Run(result.Pass());
    720 }
    721 
    722 bool ResourceMetadata::PutEntryUnderDirectory(
    723     const ResourceEntry& entry) {
    724   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    725 
    726   ResourceEntry updated_entry(entry);
    727 
    728   // The entry name may have been changed due to prior name de-duplication.
    729   // We need to first restore the file name based on the title before going
    730   // through name de-duplication again when it is added to another directory.
    731   SetBaseNameFromTitle(&updated_entry);
    732 
    733   // Do file name de-duplication - Keep changing |entry|'s name until there is
    734   // no other entry with the same name under the parent.
    735   int modifier = 0;
    736   std::string new_base_name = updated_entry.base_name();
    737   while (true) {
    738     const std::string existing_entry_id =
    739         storage_->GetChild(entry.parent_resource_id(), new_base_name);
    740     if (existing_entry_id.empty() || existing_entry_id == entry.resource_id())
    741       break;
    742 
    743     base::FilePath new_path =
    744         base::FilePath::FromUTF8Unsafe(updated_entry.base_name());
    745     new_path =
    746         new_path.InsertBeforeExtension(base::StringPrintf(" (%d)", ++modifier));
    747     // The new filename must be different from the previous one.
    748     DCHECK_NE(new_base_name, new_path.AsUTF8Unsafe());
    749     new_base_name = new_path.AsUTF8Unsafe();
    750   }
    751   updated_entry.set_base_name(new_base_name);
    752 
    753   // Add the entry to resource map.
    754   return storage_->PutEntry(updated_entry);
    755 }
    756 
    757 bool ResourceMetadata::RemoveEntryRecursively(
    758     const std::string& resource_id) {
    759   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
    760 
    761   ResourceEntry entry;
    762   if (!storage_->GetEntry(resource_id, &entry))
    763     return false;
    764 
    765   if (entry.file_info().is_directory()) {
    766     std::vector<std::string> children;
    767     storage_->GetChildren(resource_id, &children);
    768     for (size_t i = 0; i < children.size(); ++i) {
    769       if (!RemoveEntryRecursively(children[i]))
    770         return false;
    771     }
    772   }
    773   return storage_->RemoveEntry(resource_id);
    774 }
    775 
    776 }  // namespace internal
    777 }  // namespace drive
    778