Home | History | Annotate | Download | only in file_system_provider
      1 // Copyright 2014 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/file_system_provider/fake_provided_file_system.h"
      6 
      7 #include "base/files/file.h"
      8 #include "base/message_loop/message_loop_proxy.h"
      9 #include "net/base/io_buffer.h"
     10 
     11 namespace chromeos {
     12 namespace file_system_provider {
     13 namespace {
     14 
     15 const char kFakeFileName[] = "hello.txt";
     16 const char kFakeFileText[] =
     17     "This is a testing file. Lorem ipsum dolor sit amet est.";
     18 const size_t kFakeFileSize = sizeof(kFakeFileText) - 1u;
     19 const char kFakeFileModificationTime[] = "Fri Apr 25 01:47:53 UTC 2014";
     20 const char kFakeFileMimeType[] = "text/plain";
     21 
     22 }  // namespace
     23 
     24 const char kFakeFilePath[] = "/hello.txt";
     25 
     26 FakeEntry::FakeEntry() {
     27 }
     28 
     29 FakeEntry::FakeEntry(scoped_ptr<EntryMetadata> metadata,
     30                      const std::string& contents)
     31     : metadata(metadata.Pass()), contents(contents) {
     32 }
     33 
     34 FakeEntry::~FakeEntry() {
     35 }
     36 
     37 FakeProvidedFileSystem::FakeProvidedFileSystem(
     38     const ProvidedFileSystemInfo& file_system_info)
     39     : file_system_info_(file_system_info),
     40       last_file_handle_(0),
     41       weak_ptr_factory_(this) {
     42   AddEntry(
     43       base::FilePath::FromUTF8Unsafe("/"), true, "", 0, base::Time(), "", "");
     44 
     45   base::Time modification_time;
     46   DCHECK(base::Time::FromString(kFakeFileModificationTime, &modification_time));
     47   AddEntry(base::FilePath::FromUTF8Unsafe(kFakeFilePath),
     48            false,
     49            kFakeFileName,
     50            kFakeFileSize,
     51            modification_time,
     52            kFakeFileMimeType,
     53            kFakeFileText);
     54 }
     55 
     56 FakeProvidedFileSystem::~FakeProvidedFileSystem() {}
     57 
     58 void FakeProvidedFileSystem::AddEntry(const base::FilePath& entry_path,
     59                                       bool is_directory,
     60                                       const std::string& name,
     61                                       int64 size,
     62                                       base::Time modification_time,
     63                                       std::string mime_type,
     64                                       std::string contents) {
     65   DCHECK(entries_.find(entry_path) == entries_.end());
     66   scoped_ptr<EntryMetadata> metadata(new EntryMetadata);
     67 
     68   metadata->is_directory = is_directory;
     69   metadata->name = name;
     70   metadata->size = size;
     71   metadata->modification_time = modification_time;
     72   metadata->mime_type = mime_type;
     73 
     74   entries_[entry_path] =
     75       make_linked_ptr(new FakeEntry(metadata.Pass(), contents));
     76 }
     77 
     78 const FakeEntry* FakeProvidedFileSystem::GetEntry(
     79     const base::FilePath& entry_path) const {
     80   const Entries::const_iterator entry_it = entries_.find(entry_path);
     81   if (entry_it == entries_.end())
     82     return NULL;
     83 
     84   return entry_it->second.get();
     85 }
     86 
     87 ProvidedFileSystemInterface::AbortCallback
     88 FakeProvidedFileSystem::RequestUnmount(
     89     const storage::AsyncFileUtil::StatusCallback& callback) {
     90   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
     91 }
     92 
     93 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::GetMetadata(
     94     const base::FilePath& entry_path,
     95     ProvidedFileSystemInterface::MetadataFieldMask fields,
     96     const ProvidedFileSystemInterface::GetMetadataCallback& callback) {
     97   const Entries::const_iterator entry_it = entries_.find(entry_path);
     98 
     99   if (entry_it == entries_.end()) {
    100     return PostAbortableTask(
    101         base::Bind(callback,
    102                    base::Passed(make_scoped_ptr<EntryMetadata>(NULL)),
    103                    base::File::FILE_ERROR_NOT_FOUND));
    104   }
    105 
    106   scoped_ptr<EntryMetadata> metadata(new EntryMetadata);
    107   metadata->is_directory = entry_it->second->metadata->is_directory;
    108   metadata->name = entry_it->second->metadata->name;
    109   metadata->size = entry_it->second->metadata->size;
    110   metadata->modification_time = entry_it->second->metadata->modification_time;
    111   metadata->mime_type = entry_it->second->metadata->mime_type;
    112   metadata->thumbnail = entry_it->second->metadata->thumbnail;
    113 
    114   return PostAbortableTask(
    115       base::Bind(callback, base::Passed(&metadata), base::File::FILE_OK));
    116 }
    117 
    118 ProvidedFileSystemInterface::AbortCallback
    119 FakeProvidedFileSystem::ReadDirectory(
    120     const base::FilePath& directory_path,
    121     const storage::AsyncFileUtil::ReadDirectoryCallback& callback) {
    122   storage::AsyncFileUtil::EntryList entry_list;
    123 
    124   for (Entries::const_iterator it = entries_.begin(); it != entries_.end();
    125        ++it) {
    126     const base::FilePath file_path = it->first;
    127     if (file_path == directory_path || directory_path.IsParent(file_path)) {
    128       const EntryMetadata* const metadata = it->second->metadata.get();
    129       entry_list.push_back(storage::DirectoryEntry(
    130           metadata->name,
    131           metadata->is_directory ? storage::DirectoryEntry::DIRECTORY
    132                                  : storage::DirectoryEntry::FILE,
    133           metadata->size,
    134           metadata->modification_time));
    135     }
    136   }
    137 
    138   return PostAbortableTask(base::Bind(
    139       callback, base::File::FILE_OK, entry_list, false /* has_more */));
    140 }
    141 
    142 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::OpenFile(
    143     const base::FilePath& entry_path,
    144     OpenFileMode mode,
    145     const OpenFileCallback& callback) {
    146   const Entries::const_iterator entry_it = entries_.find(entry_path);
    147 
    148   if (entry_it == entries_.end()) {
    149     return PostAbortableTask(base::Bind(
    150         callback, 0 /* file_handle */, base::File::FILE_ERROR_NOT_FOUND));
    151   }
    152 
    153   const int file_handle = ++last_file_handle_;
    154   opened_files_[file_handle] = entry_path;
    155   return PostAbortableTask(
    156       base::Bind(callback, file_handle, base::File::FILE_OK));
    157 }
    158 
    159 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::CloseFile(
    160     int file_handle,
    161     const storage::AsyncFileUtil::StatusCallback& callback) {
    162   const OpenedFilesMap::iterator opened_file_it =
    163       opened_files_.find(file_handle);
    164 
    165   if (opened_file_it == opened_files_.end()) {
    166     return PostAbortableTask(
    167         base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
    168   }
    169 
    170   opened_files_.erase(opened_file_it);
    171   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    172 }
    173 
    174 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::ReadFile(
    175     int file_handle,
    176     net::IOBuffer* buffer,
    177     int64 offset,
    178     int length,
    179     const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
    180   const OpenedFilesMap::iterator opened_file_it =
    181       opened_files_.find(file_handle);
    182 
    183   if (opened_file_it == opened_files_.end() ||
    184       opened_file_it->second.AsUTF8Unsafe() != kFakeFilePath) {
    185     return PostAbortableTask(
    186         base::Bind(callback,
    187                    0 /* chunk_length */,
    188                    false /* has_more */,
    189                    base::File::FILE_ERROR_INVALID_OPERATION));
    190   }
    191 
    192   const Entries::const_iterator entry_it =
    193       entries_.find(opened_file_it->second);
    194   if (entry_it == entries_.end()) {
    195     return PostAbortableTask(
    196         base::Bind(callback,
    197                    0 /* chunk_length */,
    198                    false /* has_more */,
    199                    base::File::FILE_ERROR_INVALID_OPERATION));
    200   }
    201 
    202   // Send the response byte by byte.
    203   int64 current_offset = offset;
    204   int current_length = length;
    205 
    206   // Reading behind EOF is fine, it will just return 0 bytes.
    207   if (current_offset >= entry_it->second->metadata->size || !current_length) {
    208     return PostAbortableTask(base::Bind(callback,
    209                                         0 /* chunk_length */,
    210                                         false /* has_more */,
    211                                         base::File::FILE_OK));
    212   }
    213 
    214   const FakeEntry* const entry = entry_it->second.get();
    215   std::vector<int> task_ids;
    216   while (current_offset < entry->metadata->size && current_length) {
    217     buffer->data()[current_offset - offset] = entry->contents[current_offset];
    218     const bool has_more =
    219         (current_offset + 1 < entry->metadata->size) && (current_length - 1);
    220     const int task_id = tracker_.PostTask(
    221         base::MessageLoopProxy::current().get(),
    222         FROM_HERE,
    223         base::Bind(
    224             callback, 1 /* chunk_length */, has_more, base::File::FILE_OK));
    225     task_ids.push_back(task_id);
    226     current_offset++;
    227     current_length--;
    228   }
    229 
    230   return base::Bind(&FakeProvidedFileSystem::AbortMany,
    231                     weak_ptr_factory_.GetWeakPtr(),
    232                     task_ids);
    233 }
    234 
    235 ProvidedFileSystemInterface::AbortCallback
    236 FakeProvidedFileSystem::CreateDirectory(
    237     const base::FilePath& directory_path,
    238     bool recursive,
    239     const storage::AsyncFileUtil::StatusCallback& callback) {
    240   // TODO(mtomasz): Implement it once needed.
    241   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    242 }
    243 
    244 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::DeleteEntry(
    245     const base::FilePath& entry_path,
    246     bool recursive,
    247     const storage::AsyncFileUtil::StatusCallback& callback) {
    248   // TODO(mtomasz): Implement it once needed.
    249   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    250 }
    251 
    252 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::CreateFile(
    253     const base::FilePath& file_path,
    254     const storage::AsyncFileUtil::StatusCallback& callback) {
    255   const base::File::Error result = file_path.AsUTF8Unsafe() != kFakeFilePath
    256                                        ? base::File::FILE_ERROR_EXISTS
    257                                        : base::File::FILE_OK;
    258 
    259   return PostAbortableTask(base::Bind(callback, result));
    260 }
    261 
    262 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::CopyEntry(
    263     const base::FilePath& source_path,
    264     const base::FilePath& target_path,
    265     const storage::AsyncFileUtil::StatusCallback& callback) {
    266   // TODO(mtomasz): Implement it once needed.
    267   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    268 }
    269 
    270 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::MoveEntry(
    271     const base::FilePath& source_path,
    272     const base::FilePath& target_path,
    273     const storage::AsyncFileUtil::StatusCallback& callback) {
    274   // TODO(mtomasz): Implement it once needed.
    275   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    276 }
    277 
    278 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::Truncate(
    279     const base::FilePath& file_path,
    280     int64 length,
    281     const storage::AsyncFileUtil::StatusCallback& callback) {
    282   // TODO(mtomasz): Implement it once needed.
    283   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    284 }
    285 
    286 ProvidedFileSystemInterface::AbortCallback FakeProvidedFileSystem::WriteFile(
    287     int file_handle,
    288     net::IOBuffer* buffer,
    289     int64 offset,
    290     int length,
    291     const storage::AsyncFileUtil::StatusCallback& callback) {
    292   const OpenedFilesMap::iterator opened_file_it =
    293       opened_files_.find(file_handle);
    294 
    295   if (opened_file_it == opened_files_.end() ||
    296       opened_file_it->second.AsUTF8Unsafe() != kFakeFilePath) {
    297     return PostAbortableTask(
    298         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
    299   }
    300 
    301   const Entries::iterator entry_it = entries_.find(opened_file_it->second);
    302   if (entry_it == entries_.end()) {
    303     return PostAbortableTask(
    304         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
    305   }
    306 
    307   FakeEntry* const entry = entry_it->second.get();
    308   if (offset > entry->metadata->size) {
    309     return PostAbortableTask(
    310         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
    311   }
    312 
    313   // Allocate the string size in advance.
    314   if (offset + length > entry->metadata->size) {
    315     entry->metadata->size = offset + length;
    316     entry->contents.resize(entry->metadata->size);
    317   }
    318 
    319   entry->contents.replace(offset, length, buffer->data(), length);
    320 
    321   return PostAbortableTask(base::Bind(callback, base::File::FILE_OK));
    322 }
    323 
    324 const ProvidedFileSystemInfo& FakeProvidedFileSystem::GetFileSystemInfo()
    325     const {
    326   return file_system_info_;
    327 }
    328 
    329 RequestManager* FakeProvidedFileSystem::GetRequestManager() {
    330   NOTREACHED();
    331   return NULL;
    332 }
    333 
    334 ProvidedFileSystemInterface* FakeProvidedFileSystem::Create(
    335     Profile* profile,
    336     const ProvidedFileSystemInfo& file_system_info) {
    337   return new FakeProvidedFileSystem(file_system_info);
    338 }
    339 
    340 base::WeakPtr<ProvidedFileSystemInterface>
    341 FakeProvidedFileSystem::GetWeakPtr() {
    342   return weak_ptr_factory_.GetWeakPtr();
    343 }
    344 
    345 ProvidedFileSystemInterface::AbortCallback
    346 FakeProvidedFileSystem::PostAbortableTask(const base::Closure& callback) {
    347   const int task_id = tracker_.PostTask(
    348       base::MessageLoopProxy::current().get(), FROM_HERE, callback);
    349   return base::Bind(
    350       &FakeProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), task_id);
    351 }
    352 
    353 void FakeProvidedFileSystem::Abort(
    354     int task_id,
    355     const storage::AsyncFileUtil::StatusCallback& callback) {
    356   tracker_.TryCancel(task_id);
    357   callback.Run(base::File::FILE_OK);
    358 }
    359 
    360 void FakeProvidedFileSystem::AbortMany(
    361     const std::vector<int>& task_ids,
    362     const storage::AsyncFileUtil::StatusCallback& callback) {
    363   for (size_t i = 0; i < task_ids.size(); ++i) {
    364     tracker_.TryCancel(task_ids[i]);
    365   }
    366   callback.Run(base::File::FILE_OK);
    367 }
    368 
    369 }  // namespace file_system_provider
    370 }  // namespace chromeos
    371