Home | History | Annotate | Download | only in file_system
      1 // Copyright 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_file_operation.h"
      6 
      7 #include <string>
      8 
      9 #include "base/file_util.h"
     10 #include "chrome/browser/chromeos/drive/drive.pb.h"
     11 #include "chrome/browser/chromeos/drive/file_cache.h"
     12 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
     13 #include "chrome/browser/chromeos/drive/file_system_util.h"
     14 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     15 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     16 #include "chrome/browser/chromeos/drive/resource_metadata.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "net/base/mime_util.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace drive {
     23 namespace file_system {
     24 
     25 namespace {
     26 
     27 const char kMimeTypeOctetStream[] = "application/octet-stream";
     28 
     29 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
     30 // of the operation, before server-side file creation.
     31 FileError CheckPreConditionForCreateFile(internal::ResourceMetadata* metadata,
     32                                          const base::FilePath& file_path,
     33                                          bool is_exclusive,
     34                                          std::string* parent_resource_id,
     35                                          std::string* mime_type) {
     36   DCHECK(metadata);
     37   DCHECK(parent_resource_id);
     38   DCHECK(mime_type);
     39 
     40   ResourceEntry entry;
     41   FileError error = metadata->GetResourceEntryByPath(file_path, &entry);
     42   if (error == FILE_ERROR_OK) {
     43     // Error if an exclusive mode is requested, or the entry is not a file.
     44     return (is_exclusive ||
     45             entry.file_info().is_directory() ||
     46             entry.file_specific_info().is_hosted_document()) ?
     47         FILE_ERROR_EXISTS : FILE_ERROR_OK;
     48   }
     49 
     50   // If the file is not found, an actual request to create a new file will be
     51   // sent to the server.
     52   if (error == FILE_ERROR_NOT_FOUND) {
     53     // If parent path is not a directory, it is an error.
     54     ResourceEntry parent;
     55     if (metadata->GetResourceEntryByPath(
     56             file_path.DirName(), &parent) != FILE_ERROR_OK ||
     57         !parent.file_info().is_directory())
     58       return FILE_ERROR_NOT_A_DIRECTORY;
     59 
     60     // In the request, parent_resource_id and mime_type are needed.
     61     // Here, populate them.
     62     *parent_resource_id = parent.resource_id();
     63 
     64     // If mime type is unsure, use octet stream by default.
     65     if (!net::GetMimeTypeFromFile(file_path, mime_type))
     66       *mime_type = kMimeTypeOctetStream;
     67   }
     68 
     69   return error;
     70 }
     71 
     72 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
     73 // of the operation, after server side file creation.
     74 FileError UpdateLocalStateForCreateFile(
     75     internal::ResourceMetadata* metadata,
     76     internal::FileCache* cache,
     77     scoped_ptr<google_apis::ResourceEntry> resource_entry,
     78     base::FilePath* file_path) {
     79   DCHECK(metadata);
     80   DCHECK(cache);
     81   DCHECK(resource_entry);
     82   DCHECK(file_path);
     83 
     84   // Add the entry to the local resource metadata.
     85   FileError error = FILE_ERROR_NOT_A_FILE;
     86   ResourceEntry entry;
     87   if (ConvertToResourceEntry(*resource_entry, &entry))
     88     error = metadata->AddEntry(entry);
     89 
     90   // Depending on timing, the metadata may have inserted via change list
     91   // already. So, FILE_ERROR_EXISTS is not an error.
     92   if (error == FILE_ERROR_EXISTS)
     93     error = FILE_ERROR_OK;
     94 
     95   if (error == FILE_ERROR_OK) {
     96     // At this point, upload to the server is fully succeeded.
     97     // Populate the |file_path| which will be used to notify the observer.
     98     *file_path = metadata->GetFilePath(entry.resource_id());
     99 
    100     // Also store an empty file to the cache.
    101     // Here, failure is not a fatal error, so ignore the returned code.
    102     FileError cache_store_error = FILE_ERROR_FAILED;
    103     base::FilePath empty_file;
    104     if (file_util::CreateTemporaryFile(&empty_file)) {
    105       cache_store_error =  cache->Store(
    106           entry.resource_id(),
    107           entry.file_specific_info().md5(),
    108           empty_file,
    109           internal::FileCache::FILE_OPERATION_MOVE);
    110     }
    111     DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
    112         << "Failed to store a cache file: "
    113         << FileErrorToString(cache_store_error)
    114         << ", resource_id: " << entry.resource_id();
    115   }
    116 
    117   return error;
    118 }
    119 
    120 }  // namespace
    121 
    122 CreateFileOperation::CreateFileOperation(
    123     base::SequencedTaskRunner* blocking_task_runner,
    124     OperationObserver* observer,
    125     JobScheduler* scheduler,
    126     internal::ResourceMetadata* metadata,
    127     internal::FileCache* cache)
    128     : blocking_task_runner_(blocking_task_runner),
    129       observer_(observer),
    130       scheduler_(scheduler),
    131       metadata_(metadata),
    132       cache_(cache),
    133       weak_ptr_factory_(this) {
    134   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    135 }
    136 
    137 CreateFileOperation::~CreateFileOperation() {
    138   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    139 }
    140 
    141 void CreateFileOperation::CreateFile(const base::FilePath& file_path,
    142                                      bool is_exclusive,
    143                                      const FileOperationCallback& callback) {
    144   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    145   DCHECK(!callback.is_null());
    146 
    147   std::string* parent_resource_id = new std::string;
    148   std::string* mime_type = new std::string;
    149   base::PostTaskAndReplyWithResult(
    150       blocking_task_runner_.get(),
    151       FROM_HERE,
    152       base::Bind(&CheckPreConditionForCreateFile,
    153                  metadata_,
    154                  file_path,
    155                  is_exclusive,
    156                  parent_resource_id,
    157                  mime_type),
    158       base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
    159                  weak_ptr_factory_.GetWeakPtr(),
    160                  file_path,
    161                  callback,
    162                  base::Owned(parent_resource_id),
    163                  base::Owned(mime_type)));
    164 }
    165 
    166 void CreateFileOperation::CreateFileAfterCheckPreCondition(
    167     const base::FilePath& file_path,
    168     const FileOperationCallback& callback,
    169     std::string* parent_resource_id,
    170     std::string* mime_type,
    171     FileError error) {
    172   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    173   DCHECK(!callback.is_null());
    174   DCHECK(parent_resource_id);
    175   DCHECK(mime_type);
    176 
    177   // If the file is found, or an error other than "not found" is found,
    178   // runs callback and quit the operation.
    179   if (error != FILE_ERROR_NOT_FOUND) {
    180     callback.Run(error);
    181     return;
    182   }
    183 
    184   scheduler_->CreateFile(
    185       *parent_resource_id,
    186       file_path,
    187       file_path.BaseName().value(),
    188       *mime_type,
    189       ClientContext(USER_INITIATED),
    190       base::Bind(&CreateFileOperation::CreateFileAfterUpload,
    191                  weak_ptr_factory_.GetWeakPtr(),
    192                  callback));
    193 }
    194 
    195 void CreateFileOperation::CreateFileAfterUpload(
    196     const FileOperationCallback& callback,
    197     google_apis::GDataErrorCode gdata_error,
    198     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
    199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    200   DCHECK(!callback.is_null());
    201 
    202   FileError error = GDataToFileError(gdata_error);
    203   if (error != FILE_ERROR_OK) {
    204     callback.Run(error);
    205     return;
    206   }
    207   DCHECK(resource_entry);
    208 
    209   base::FilePath* file_path = new base::FilePath;
    210   base::PostTaskAndReplyWithResult(
    211       blocking_task_runner_.get(),
    212       FROM_HERE,
    213       base::Bind(&UpdateLocalStateForCreateFile,
    214                  metadata_,
    215                  cache_,
    216                  base::Passed(&resource_entry),
    217                  file_path),
    218       base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
    219                  weak_ptr_factory_.GetWeakPtr(),
    220                  callback,
    221                  base::Owned(file_path)));
    222 }
    223 
    224 void CreateFileOperation::CreateFileAfterUpdateLocalState(
    225     const FileOperationCallback& callback,
    226     base::FilePath* file_path,
    227     FileError error) {
    228   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    229   DCHECK(!callback.is_null());
    230   DCHECK(file_path);
    231 
    232   // Notify observer if the file creation process is successfully done.
    233   if (error == FILE_ERROR_OK)
    234     observer_->OnDirectoryChangedByOperation(file_path->DirName());
    235 
    236   callback.Run(error);
    237 }
    238 
    239 }  // namespace file_system
    240 }  // namespace drive
    241