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 not set or "application/octet-stream", guess from the
     65     // |file_path|. If it is still unsure, use octet-stream by default.
     66     if ((mime_type->empty() || *mime_type == kMimeTypeOctetStream) &&
     67         !net::GetMimeTypeFromFile(file_path, mime_type)) {
     68       *mime_type = kMimeTypeOctetStream;
     69     }
     70   }
     71 
     72   return error;
     73 }
     74 
     75 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
     76 // of the operation, after server side file creation.
     77 FileError UpdateLocalStateForCreateFile(
     78     internal::ResourceMetadata* metadata,
     79     internal::FileCache* cache,
     80     scoped_ptr<google_apis::ResourceEntry> resource_entry,
     81     base::FilePath* file_path) {
     82   DCHECK(metadata);
     83   DCHECK(cache);
     84   DCHECK(resource_entry);
     85   DCHECK(file_path);
     86 
     87   // Add the entry to the local resource metadata.
     88   ResourceEntry entry;
     89   std::string parent_resource_id;
     90   if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
     91     return FILE_ERROR_NOT_A_FILE;
     92 
     93   std::string parent_local_id;
     94   FileError error = metadata->GetIdByResourceId(parent_resource_id,
     95                                                 &parent_local_id);
     96   if (error != FILE_ERROR_OK)
     97     return error;
     98   entry.set_parent_local_id(parent_local_id);
     99 
    100   std::string local_id;
    101   error = metadata->AddEntry(entry, &local_id);
    102 
    103   // Depending on timing, the metadata may have inserted via change list
    104   // already. So, FILE_ERROR_EXISTS is not an error.
    105   if (error == FILE_ERROR_EXISTS)
    106     error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
    107 
    108   if (error == FILE_ERROR_OK) {
    109     // At this point, upload to the server is fully succeeded.
    110     // Populate the |file_path| which will be used to notify the observer.
    111     *file_path = metadata->GetFilePath(local_id);
    112 
    113     // Also store an empty file to the cache.
    114     // Here, failure is not a fatal error, so ignore the returned code.
    115     FileError cache_store_error = FILE_ERROR_FAILED;
    116     base::FilePath empty_file;
    117     if (base::CreateTemporaryFile(&empty_file)) {
    118       cache_store_error =  cache->Store(
    119           local_id,
    120           entry.file_specific_info().md5(),
    121           empty_file,
    122           internal::FileCache::FILE_OPERATION_MOVE);
    123     }
    124     DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
    125         << "Failed to store a cache file: "
    126         << FileErrorToString(cache_store_error)
    127         << ", local_id: " << local_id;
    128   }
    129 
    130   return error;
    131 }
    132 
    133 }  // namespace
    134 
    135 CreateFileOperation::CreateFileOperation(
    136     base::SequencedTaskRunner* blocking_task_runner,
    137     OperationObserver* observer,
    138     JobScheduler* scheduler,
    139     internal::ResourceMetadata* metadata,
    140     internal::FileCache* cache)
    141     : blocking_task_runner_(blocking_task_runner),
    142       observer_(observer),
    143       scheduler_(scheduler),
    144       metadata_(metadata),
    145       cache_(cache),
    146       weak_ptr_factory_(this) {
    147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    148 }
    149 
    150 CreateFileOperation::~CreateFileOperation() {
    151   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    152 }
    153 
    154 void CreateFileOperation::CreateFile(const base::FilePath& file_path,
    155                                      bool is_exclusive,
    156                                      const std::string& mime_type,
    157                                      const FileOperationCallback& callback) {
    158   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    159   DCHECK(!callback.is_null());
    160 
    161   std::string* parent_resource_id = new std::string;
    162   std::string* determined_mime_type = new std::string(mime_type);
    163   base::PostTaskAndReplyWithResult(
    164       blocking_task_runner_.get(),
    165       FROM_HERE,
    166       base::Bind(&CheckPreConditionForCreateFile,
    167                  metadata_,
    168                  file_path,
    169                  is_exclusive,
    170                  parent_resource_id,
    171                  determined_mime_type),
    172       base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
    173                  weak_ptr_factory_.GetWeakPtr(),
    174                  file_path,
    175                  callback,
    176                  base::Owned(parent_resource_id),
    177                  base::Owned(determined_mime_type)));
    178 }
    179 
    180 void CreateFileOperation::CreateFileAfterCheckPreCondition(
    181     const base::FilePath& file_path,
    182     const FileOperationCallback& callback,
    183     std::string* parent_resource_id,
    184     std::string* mime_type,
    185     FileError error) {
    186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    187   DCHECK(!callback.is_null());
    188   DCHECK(parent_resource_id);
    189   DCHECK(mime_type);
    190 
    191   // If the file is found, or an error other than "not found" is found,
    192   // runs callback and quit the operation.
    193   if (error != FILE_ERROR_NOT_FOUND) {
    194     callback.Run(error);
    195     return;
    196   }
    197 
    198   scheduler_->CreateFile(
    199       *parent_resource_id,
    200       file_path,
    201       file_path.BaseName().value(),
    202       *mime_type,
    203       ClientContext(USER_INITIATED),
    204       base::Bind(&CreateFileOperation::CreateFileAfterUpload,
    205                  weak_ptr_factory_.GetWeakPtr(),
    206                  callback));
    207 }
    208 
    209 void CreateFileOperation::CreateFileAfterUpload(
    210     const FileOperationCallback& callback,
    211     google_apis::GDataErrorCode gdata_error,
    212     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
    213   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    214   DCHECK(!callback.is_null());
    215 
    216   FileError error = GDataToFileError(gdata_error);
    217   if (error != FILE_ERROR_OK) {
    218     callback.Run(error);
    219     return;
    220   }
    221   DCHECK(resource_entry);
    222 
    223   base::FilePath* file_path = new base::FilePath;
    224   base::PostTaskAndReplyWithResult(
    225       blocking_task_runner_.get(),
    226       FROM_HERE,
    227       base::Bind(&UpdateLocalStateForCreateFile,
    228                  metadata_,
    229                  cache_,
    230                  base::Passed(&resource_entry),
    231                  file_path),
    232       base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
    233                  weak_ptr_factory_.GetWeakPtr(),
    234                  callback,
    235                  base::Owned(file_path)));
    236 }
    237 
    238 void CreateFileOperation::CreateFileAfterUpdateLocalState(
    239     const FileOperationCallback& callback,
    240     base::FilePath* file_path,
    241     FileError error) {
    242   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    243   DCHECK(!callback.is_null());
    244   DCHECK(file_path);
    245 
    246   // Notify observer if the file creation process is successfully done.
    247   if (error == FILE_ERROR_OK)
    248     observer_->OnDirectoryChangedByOperation(file_path->DirName());
    249 
    250   callback.Run(error);
    251 }
    252 
    253 }  // namespace file_system
    254 }  // namespace drive
    255