Home | History | Annotate | Download | only in file_system
      1 // Copyright (c) 2013 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/file_system/create_directory_operation.h"
      6 
      7 #include "chrome/browser/chromeos/drive/drive.pb.h"
      8 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
      9 #include "chrome/browser/chromeos/drive/file_system_util.h"
     10 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     11 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     12 #include "chrome/browser/google_apis/gdata_errorcode.h"
     13 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
     14 #include "content/public/browser/browser_thread.h"
     15 
     16 using content::BrowserThread;
     17 
     18 namespace drive {
     19 namespace file_system {
     20 
     21 namespace {
     22 
     23 // Part of CreateDirectoryRecursively(). Adds an |entry| for new directory
     24 // to |metadata|, and return the status. If succeeded, |file_path| will store
     25 // the path to the result file.
     26 FileError UpdateLocalStateForCreateDirectoryRecursively(
     27     internal::ResourceMetadata* metadata,
     28     const ResourceEntry& entry,
     29     base::FilePath* file_path) {
     30   DCHECK(metadata);
     31   DCHECK(file_path);
     32 
     33   FileError result = metadata->AddEntry(entry);
     34   // Depending on timing, a metadata may be updated by change list already.
     35   // So, FILE_ERROR_EXISTS is not an error.
     36   if (result == FILE_ERROR_EXISTS)
     37     result = FILE_ERROR_OK;
     38 
     39   if (result == FILE_ERROR_OK)
     40     *file_path = metadata->GetFilePath(entry.resource_id());
     41 
     42   return result;
     43 }
     44 
     45 }  // namespace
     46 
     47 CreateDirectoryOperation::CreateDirectoryOperation(
     48     base::SequencedTaskRunner* blocking_task_runner,
     49     OperationObserver* observer,
     50     JobScheduler* scheduler,
     51     internal::ResourceMetadata* metadata)
     52     : blocking_task_runner_(blocking_task_runner),
     53       observer_(observer),
     54       scheduler_(scheduler),
     55       metadata_(metadata),
     56       weak_ptr_factory_(this) {
     57   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     58 }
     59 
     60 CreateDirectoryOperation::~CreateDirectoryOperation() {
     61   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     62 }
     63 
     64 void CreateDirectoryOperation::CreateDirectory(
     65     const base::FilePath& directory_path,
     66     bool is_exclusive,
     67     bool is_recursive,
     68     const FileOperationCallback& callback) {
     69   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     70   DCHECK(!callback.is_null());
     71 
     72   ResourceEntry* entry = new ResourceEntry;
     73   base::PostTaskAndReplyWithResult(
     74       blocking_task_runner_.get(),
     75       FROM_HERE,
     76       base::Bind(&CreateDirectoryOperation::GetExistingDeepestDirectory,
     77                  metadata_,
     78                  directory_path,
     79                  entry),
     80       base::Bind(&CreateDirectoryOperation::
     81                      CreateDirectoryAfterGetExistingDeepestDirectory,
     82                  weak_ptr_factory_.GetWeakPtr(),
     83                  directory_path,
     84                  is_exclusive,
     85                  is_recursive,
     86                  callback,
     87                  base::Owned(entry)));
     88 }
     89 
     90 // static
     91 base::FilePath CreateDirectoryOperation::GetExistingDeepestDirectory(
     92     internal::ResourceMetadata* metadata,
     93     const base::FilePath& directory_path,
     94     ResourceEntry* entry) {
     95   DCHECK(metadata);
     96   DCHECK(entry);
     97 
     98   std::vector<base::FilePath::StringType> components;
     99   directory_path.GetComponents(&components);
    100 
    101   if (components.empty() || components[0] != util::kDriveGrandRootDirName)
    102     return base::FilePath();
    103 
    104   std::string resource_id = util::kDriveGrandRootSpecialResourceId;
    105   for (size_t i = 1; i < components.size(); ++i) {
    106     std::string child_resource_id =
    107         metadata->GetChildResourceId(resource_id, components[i]);
    108     if (child_resource_id.empty())
    109       break;
    110     resource_id = child_resource_id;
    111   }
    112 
    113   FileError error = metadata->GetResourceEntryById(resource_id, entry);
    114   DCHECK_EQ(FILE_ERROR_OK, error);
    115 
    116   if (!entry->file_info().is_directory())
    117     return base::FilePath();
    118 
    119   return metadata->GetFilePath(resource_id);
    120 }
    121 
    122 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory(
    123     const base::FilePath& directory_path,
    124     bool is_exclusive,
    125     bool is_recursive,
    126     const FileOperationCallback& callback,
    127     ResourceEntry* existing_deepest_directory_entry,
    128     const base::FilePath& existing_deepest_directory_path) {
    129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    130   DCHECK(!callback.is_null());
    131   DCHECK(existing_deepest_directory_entry);
    132 
    133   if (existing_deepest_directory_path.empty()) {
    134     callback.Run(FILE_ERROR_NOT_FOUND);
    135     return;
    136   }
    137 
    138   if (directory_path == existing_deepest_directory_path) {
    139     callback.Run(is_exclusive ? FILE_ERROR_EXISTS : FILE_ERROR_OK);
    140     return;
    141   }
    142 
    143   // If it is not recursive creation, the found directory must be the direct
    144   // parent of |directory_path| to ensure creating exact one directory.
    145   if (!is_recursive &&
    146       existing_deepest_directory_path != directory_path.DirName()) {
    147     callback.Run(FILE_ERROR_NOT_FOUND);
    148     return;
    149   }
    150 
    151   // Create directories under the found directory.
    152   base::FilePath remaining_path;
    153   existing_deepest_directory_path.AppendRelativePath(
    154       directory_path, &remaining_path);
    155   CreateDirectoryRecursively(existing_deepest_directory_entry->resource_id(),
    156                              remaining_path, callback);
    157 }
    158 
    159 void CreateDirectoryOperation::CreateDirectoryRecursively(
    160     const std::string& parent_resource_id,
    161     const base::FilePath& relative_file_path,
    162     const FileOperationCallback& callback) {
    163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    164   DCHECK(!callback.is_null());
    165 
    166   // Split the first component and remaining ones of |relative_file_path|.
    167   std::vector<base::FilePath::StringType> components;
    168   relative_file_path.GetComponents(&components);
    169   DCHECK(!components.empty());
    170   base::FilePath title(components[0]);
    171   base::FilePath remaining_path;
    172   title.AppendRelativePath(relative_file_path, &remaining_path);
    173 
    174   scheduler_->AddNewDirectory(
    175       parent_resource_id,
    176       title.AsUTF8Unsafe(),
    177       base::Bind(&CreateDirectoryOperation
    178                      ::CreateDirectoryRecursivelyAfterAddNewDirectory,
    179                  weak_ptr_factory_.GetWeakPtr(), remaining_path, callback));
    180 }
    181 
    182 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterAddNewDirectory(
    183     const base::FilePath& remaining_path,
    184     const FileOperationCallback& callback,
    185     google_apis::GDataErrorCode gdata_error,
    186     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
    187   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    188   DCHECK(!callback.is_null());
    189 
    190   FileError error = GDataToFileError(gdata_error);
    191   if (error != FILE_ERROR_OK) {
    192     callback.Run(error);
    193     return;
    194   }
    195   DCHECK(resource_entry);
    196 
    197   ResourceEntry entry;
    198   if (!ConvertToResourceEntry(*resource_entry, &entry)) {
    199     callback.Run(FILE_ERROR_NOT_A_FILE);
    200     return;
    201   }
    202 
    203   // Note that the created directory may be renamed inside
    204   // ResourceMetadata::AddEntry due to name confliction.
    205   // What we actually need here is the new created path (not the path we try
    206   // to create).
    207   base::FilePath* file_path = new base::FilePath;
    208   base::PostTaskAndReplyWithResult(
    209       blocking_task_runner_.get(),
    210       FROM_HERE,
    211       base::Bind(&UpdateLocalStateForCreateDirectoryRecursively,
    212                  metadata_,
    213                  entry,
    214                  file_path),
    215       base::Bind(&CreateDirectoryOperation::
    216                      CreateDirectoryRecursivelyAfterUpdateLocalState,
    217                  weak_ptr_factory_.GetWeakPtr(),
    218                  resource_entry->resource_id(),
    219                  remaining_path,
    220                  callback,
    221                  base::Owned(file_path)));
    222 }
    223 
    224 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterUpdateLocalState(
    225     const std::string& resource_id,
    226     const base::FilePath& remaining_path,
    227     const FileOperationCallback& callback,
    228     base::FilePath* file_path,
    229     FileError error) {
    230   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    231   DCHECK(!callback.is_null());
    232 
    233   if (error != FILE_ERROR_OK) {
    234     callback.Run(error);
    235     return;
    236   }
    237 
    238   observer_->OnDirectoryChangedByOperation(file_path->DirName());
    239 
    240   if (remaining_path.empty()) {
    241     // All directories are created successfully.
    242     callback.Run(FILE_ERROR_OK);
    243     return;
    244   }
    245 
    246   // Create descendant directories.
    247   CreateDirectoryRecursively(resource_id, remaining_path, callback);
    248 }
    249 
    250 }  // namespace file_system
    251 }  // namespace drive
    252