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