1 // Copyright 2017 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/smb_client/smb_file_system.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/task/post_task.h"
16 #include "base/task/thread_pool.h"
17 #include "chrome/browser/chromeos/file_system_provider/service.h"
18 #include "chrome/browser/chromeos/smb_client/smb_errors.h"
19 #include "chrome/browser/chromeos/smb_client/smb_file_system_id.h"
20 #include "chromeos/dbus/dbus_thread_manager.h"
21 #include "chromeos/dbus/smb_provider_client.h"
22 #include "components/services/filesystem/public/mojom/types.mojom.h"
23 #include "net/base/io_buffer.h"
24
25 namespace chromeos {
26
27 namespace {
28
29 using file_system_provider::ProvidedFileSystemInterface;
30
31 // This is a bogus data URI.
32 // The Files app will attempt to download a whole image to create a thumbnail
33 // any time you visit a folder. A bug (crbug.com/548050) tracks not doing that
34 // for NETWORK providers. This work around is to supply an icon but make it
35 // bogus so it falls back to the generic icon.
36 constexpr char kUnknownImageDataUri[] = "data:image/png;base64,X";
37
38 // Initial number of entries to send during read directory. This number is
39 // smaller than kReadDirectoryMaxBatchSize since we want the initial page to
40 // load as quickly as possible.
41 constexpr uint32_t kReadDirectoryInitialBatchSize = 64;
42
43 // Maximum number of entries to send at a time for read directory,
44 constexpr uint32_t kReadDirectoryMaxBatchSize = 2048;
45
46 // Capacity of the task queue. Represents how many operations can be in-flight
47 // over D-Bus concurrently.
48 constexpr size_t kTaskQueueCapacity = 2;
49
50 constexpr ProvidedFileSystemInterface::MetadataFieldMask kRequestableFields =
51 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_IS_DIRECTORY |
52 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_NAME |
53 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_SIZE |
54 ProvidedFileSystemInterface::MetadataField::
55 METADATA_FIELD_MODIFICATION_TIME;
56
RequestedIsDirectory(ProvidedFileSystemInterface::MetadataFieldMask fields)57 bool RequestedIsDirectory(
58 ProvidedFileSystemInterface::MetadataFieldMask fields) {
59 return fields & ProvidedFileSystemInterface::MetadataField::
60 METADATA_FIELD_IS_DIRECTORY;
61 }
62
RequestedName(ProvidedFileSystemInterface::MetadataFieldMask fields)63 bool RequestedName(ProvidedFileSystemInterface::MetadataFieldMask fields) {
64 return fields &
65 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_NAME;
66 }
67
RequestedSize(ProvidedFileSystemInterface::MetadataFieldMask fields)68 bool RequestedSize(ProvidedFileSystemInterface::MetadataFieldMask fields) {
69 return fields &
70 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_SIZE;
71 }
72
RequestedModificationTime(ProvidedFileSystemInterface::MetadataFieldMask fields)73 bool RequestedModificationTime(
74 ProvidedFileSystemInterface::MetadataFieldMask fields) {
75 return fields & ProvidedFileSystemInterface::MetadataField::
76 METADATA_FIELD_MODIFICATION_TIME;
77 }
78
RequestedThumbnail(ProvidedFileSystemInterface::MetadataFieldMask fields)79 bool RequestedThumbnail(ProvidedFileSystemInterface::MetadataFieldMask fields) {
80 return fields &
81 ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_THUMBNAIL;
82 }
83
IsRedundantRequest(ProvidedFileSystemInterface::MetadataFieldMask fields)84 bool IsRedundantRequest(ProvidedFileSystemInterface::MetadataFieldMask fields) {
85 // If there isn't at least 1 requestable field there is no point doing a
86 // network request.
87 return (fields & kRequestableFields) == 0;
88 }
89
MapEntryType(bool is_directory)90 filesystem::mojom::FsFileType MapEntryType(bool is_directory) {
91 return is_directory ? filesystem::mojom::FsFileType::DIRECTORY
92 : filesystem::mojom::FsFileType::REGULAR_FILE;
93 }
94
CreateTempFileManager()95 std::unique_ptr<smb_client::TempFileManager> CreateTempFileManager() {
96 return std::make_unique<smb_client::TempFileManager>();
97 }
98
ConvertEntries(const smbprovider::DirectoryEntryListProto & entries_proto,storage::AsyncFileUtil::EntryList * out_entries)99 void ConvertEntries(const smbprovider::DirectoryEntryListProto& entries_proto,
100 storage::AsyncFileUtil::EntryList* out_entries) {
101 DCHECK(out_entries);
102
103 for (const smbprovider::DirectoryEntryProto& entry :
104 entries_proto.entries()) {
105 out_entries->emplace_back(base::FilePath(entry.name()),
106 MapEntryType(entry.is_directory()));
107 }
108 }
109
110 } // namespace
111
112 namespace smb_client {
113
114 using file_system_provider::AbortCallback;
115
SmbFileSystem(const file_system_provider::ProvidedFileSystemInfo & file_system_info,MountIdCallback mount_id_callback,UnmountCallback unmount_callback,RequestCredentialsCallback request_creds_callback,RequestUpdatedSharePathCallback request_path_callback)116 SmbFileSystem::SmbFileSystem(
117 const file_system_provider::ProvidedFileSystemInfo& file_system_info,
118 MountIdCallback mount_id_callback,
119 UnmountCallback unmount_callback,
120 RequestCredentialsCallback request_creds_callback,
121 RequestUpdatedSharePathCallback request_path_callback)
122 : file_system_info_(file_system_info),
123 mount_id_callback_(std::move(mount_id_callback)),
124 unmount_callback_(std::move(unmount_callback)),
125 request_creds_callback_(std::move(request_creds_callback)),
126 request_path_callback_(std::move(request_path_callback)),
127 task_queue_(kTaskQueueCapacity) {}
128
~SmbFileSystem()129 SmbFileSystem::~SmbFileSystem() {}
130
GetMountId() const131 int32_t SmbFileSystem::GetMountId() const {
132 return mount_id_callback_.Run(file_system_info_);
133 }
134
GetMountPath() const135 std::string SmbFileSystem::GetMountPath() const {
136 return GetSharePathFromFileSystemId(file_system_info_.file_system_id())
137 .value();
138 }
139
GetSmbProviderClient() const140 SmbProviderClient* SmbFileSystem::GetSmbProviderClient() const {
141 return chromeos::DBusThreadManager::Get()->GetSmbProviderClient();
142 }
143
GetWeakSmbProviderClient() const144 base::WeakPtr<SmbProviderClient> SmbFileSystem::GetWeakSmbProviderClient()
145 const {
146 return GetSmbProviderClient()->AsWeakPtr();
147 }
148
EnqueueTask(SmbTask task,OperationId operation_id)149 void SmbFileSystem::EnqueueTask(SmbTask task, OperationId operation_id) {
150 task_queue_.AddTask(std::move(task), operation_id);
151 }
152
EnqueueTaskAndGetOperationId(SmbTask task)153 OperationId SmbFileSystem::EnqueueTaskAndGetOperationId(SmbTask task) {
154 OperationId operation_id = task_queue_.GetNextOperationId();
155 EnqueueTask(std::move(task), operation_id);
156 return operation_id;
157 }
158
EnqueueTaskAndGetCallback(SmbTask task)159 AbortCallback SmbFileSystem::EnqueueTaskAndGetCallback(SmbTask task) {
160 OperationId operation_id = EnqueueTaskAndGetOperationId(std::move(task));
161 return CreateAbortCallback(operation_id);
162 }
163
Abort(OperationId operation_id)164 void SmbFileSystem::Abort(OperationId operation_id) {
165 task_queue_.AbortOperation(operation_id);
166 }
167
CreateAbortCallback(OperationId operation_id)168 AbortCallback SmbFileSystem::CreateAbortCallback(OperationId operation_id) {
169 return base::BindRepeating(&SmbFileSystem::Abort, AsWeakPtr(), operation_id);
170 }
171
CreateAbortCallback()172 AbortCallback SmbFileSystem::CreateAbortCallback() {
173 return base::DoNothing();
174 }
175
RequestUnmount(storage::AsyncFileUtil::StatusCallback callback)176 AbortCallback SmbFileSystem::RequestUnmount(
177 storage::AsyncFileUtil::StatusCallback callback) {
178 auto reply = base::BindOnce(&SmbFileSystem::HandleRequestUnmountCallback,
179 AsWeakPtr(), std::move(callback));
180
181 // RequestUnmount() is called as a result of the user removing the mount from
182 // the Files app. In this case, remove any stored password to clean up state
183 // and prevent the password from being used the next time the user adds the
184 // same share.
185 SmbTask task = base::BindOnce(&SmbProviderClient::Unmount,
186 GetWeakSmbProviderClient(), GetMountId(),
187 true /* remove_password */, std::move(reply));
188
189 return EnqueueTaskAndGetCallback(std::move(task));
190 }
191
HandleRequestUnmountCallback(storage::AsyncFileUtil::StatusCallback callback,smbprovider::ErrorType error)192 void SmbFileSystem::HandleRequestUnmountCallback(
193 storage::AsyncFileUtil::StatusCallback callback,
194 smbprovider::ErrorType error) {
195 task_queue_.TaskFinished();
196 base::File::Error result = TranslateToFileError(error);
197 if (result == base::File::FILE_OK ||
198 // Mount ID wasn't found. smbprovider might have crashed and restarted.
199 // This shouldn't prevent the user from removing the share.
200 result == base::File::FILE_ERROR_NOT_FOUND ||
201 // Mount process either has not yet completed, or failed. This also
202 // shouldn't prevent the user from removing the share.
203 GetMountId() < 0) {
204 result =
205 RunUnmountCallback(file_system_info_.file_system_id(),
206 file_system_provider::Service::UNMOUNT_REASON_USER);
207 }
208 std::move(callback).Run(result);
209 }
210
GetMetadata(const base::FilePath & entry_path,ProvidedFileSystemInterface::MetadataFieldMask fields,ProvidedFileSystemInterface::GetMetadataCallback callback)211 AbortCallback SmbFileSystem::GetMetadata(
212 const base::FilePath& entry_path,
213 ProvidedFileSystemInterface::MetadataFieldMask fields,
214 ProvidedFileSystemInterface::GetMetadataCallback callback) {
215 if (IsRedundantRequest(fields)) {
216 return HandleSyncRedundantGetMetadata(fields, std::move(callback));
217 }
218
219 int32_t mount_id = GetMountId();
220 if (mount_id < 0 && entry_path.value() == "/") {
221 // If the mount process hasn't completed, return a dummy entry for the root
222 // directory. This is needed for the Files app to see the share has been
223 // mounted.
224 std::unique_ptr<file_system_provider::EntryMetadata> metadata =
225 std::make_unique<file_system_provider::EntryMetadata>();
226 if (RequestedIsDirectory(fields)) {
227 metadata->is_directory = std::make_unique<bool>(true);
228 }
229 if (RequestedName(fields)) {
230 metadata->name = std::make_unique<std::string>();
231 }
232 if (RequestedSize(fields)) {
233 metadata->size = std::make_unique<int64_t>(0);
234 }
235 if (RequestedModificationTime(fields)) {
236 metadata->modification_time = std::make_unique<base::Time>();
237 }
238 if (RequestedThumbnail(fields)) {
239 metadata->thumbnail = std::make_unique<std::string>(kUnknownImageDataUri);
240 }
241 // Mime types are not supported.
242 std::move(callback).Run(std::move(metadata), base::File::FILE_OK);
243 return CreateAbortCallback();
244 }
245
246 auto reply =
247 base::BindOnce(&SmbFileSystem::HandleRequestGetMetadataEntryCallback,
248 AsWeakPtr(), fields, std::move(callback));
249 SmbTask task = base::BindOnce(&SmbProviderClient::GetMetadataEntry,
250 GetWeakSmbProviderClient(), mount_id,
251 entry_path, std::move(reply));
252
253 return EnqueueTaskAndGetCallback(std::move(task));
254 }
255
GetActions(const std::vector<base::FilePath> & entry_paths,GetActionsCallback callback)256 AbortCallback SmbFileSystem::GetActions(
257 const std::vector<base::FilePath>& entry_paths,
258 GetActionsCallback callback) {
259 const std::vector<file_system_provider::Action> actions;
260 // No actions are currently supported.
261 std::move(callback).Run(actions, base::File::FILE_OK);
262 return CreateAbortCallback();
263 }
264
ExecuteAction(const std::vector<base::FilePath> & entry_paths,const std::string & action_id,storage::AsyncFileUtil::StatusCallback callback)265 AbortCallback SmbFileSystem::ExecuteAction(
266 const std::vector<base::FilePath>& entry_paths,
267 const std::string& action_id,
268 storage::AsyncFileUtil::StatusCallback callback) {
269 NOTREACHED();
270 return CreateAbortCallback();
271 }
272
ReadDirectory(const base::FilePath & directory_path,storage::AsyncFileUtil::ReadDirectoryCallback callback)273 AbortCallback SmbFileSystem::ReadDirectory(
274 const base::FilePath& directory_path,
275 storage::AsyncFileUtil::ReadDirectoryCallback callback) {
276 OperationId operation_id = task_queue_.GetNextOperationId();
277
278 StartReadDirectory(directory_path, operation_id, std::move(callback));
279
280 return CreateAbortCallback(operation_id);
281 }
282
OpenFile(const base::FilePath & file_path,file_system_provider::OpenFileMode mode,OpenFileCallback callback)283 AbortCallback SmbFileSystem::OpenFile(const base::FilePath& file_path,
284 file_system_provider::OpenFileMode mode,
285 OpenFileCallback callback) {
286 bool writeable =
287 mode == file_system_provider::OPEN_FILE_MODE_WRITE ? true : false;
288
289 auto reply = base::BindOnce(&SmbFileSystem::HandleRequestOpenFileCallback,
290 AsWeakPtr(), std::move(callback));
291 SmbTask task =
292 base::BindOnce(&SmbProviderClient::OpenFile, GetWeakSmbProviderClient(),
293 GetMountId(), file_path, writeable, std::move(reply));
294
295 return EnqueueTaskAndGetCallback(std::move(task));
296 }
297
HandleRequestOpenFileCallback(OpenFileCallback callback,smbprovider::ErrorType error,int32_t file_id) const298 void SmbFileSystem::HandleRequestOpenFileCallback(
299 OpenFileCallback callback,
300 smbprovider::ErrorType error,
301 int32_t file_id) const {
302 task_queue_.TaskFinished();
303 std::move(callback).Run(file_id, TranslateToFileError(error));
304 }
305
CloseFile(int file_handle,storage::AsyncFileUtil::StatusCallback callback)306 AbortCallback SmbFileSystem::CloseFile(
307 int file_handle,
308 storage::AsyncFileUtil::StatusCallback callback) {
309 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
310 std::move(callback));
311 SmbTask task =
312 base::BindOnce(&SmbProviderClient::CloseFile, GetWeakSmbProviderClient(),
313 GetMountId(), file_handle, std::move(reply));
314
315 return EnqueueTaskAndGetCallback(std::move(task));
316 }
317
ReadFile(int file_handle,net::IOBuffer * buffer,int64_t offset,int length,ReadChunkReceivedCallback callback)318 AbortCallback SmbFileSystem::ReadFile(int file_handle,
319 net::IOBuffer* buffer,
320 int64_t offset,
321 int length,
322 ReadChunkReceivedCallback callback) {
323 auto reply =
324 base::BindOnce(&SmbFileSystem::HandleRequestReadFileCallback, AsWeakPtr(),
325 length, scoped_refptr<net::IOBuffer>(buffer), callback);
326
327 SmbTask task = base::BindOnce(&SmbProviderClient::ReadFile,
328 GetWeakSmbProviderClient(), GetMountId(),
329 file_handle, offset, length, std::move(reply));
330
331 return EnqueueTaskAndGetCallback(std::move(task));
332 }
333
CreateDirectory(const base::FilePath & directory_path,bool recursive,storage::AsyncFileUtil::StatusCallback callback)334 AbortCallback SmbFileSystem::CreateDirectory(
335 const base::FilePath& directory_path,
336 bool recursive,
337 storage::AsyncFileUtil::StatusCallback callback) {
338 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
339 std::move(callback));
340
341 SmbTask task = base::BindOnce(&SmbProviderClient::CreateDirectory,
342 GetWeakSmbProviderClient(), GetMountId(),
343 directory_path, recursive, std::move(reply));
344
345 return EnqueueTaskAndGetCallback(std::move(task));
346 }
347
CreateFile(const base::FilePath & file_path,storage::AsyncFileUtil::StatusCallback callback)348 AbortCallback SmbFileSystem::CreateFile(
349 const base::FilePath& file_path,
350 storage::AsyncFileUtil::StatusCallback callback) {
351 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
352 std::move(callback));
353 SmbTask task =
354 base::BindOnce(&SmbProviderClient::CreateFile, GetWeakSmbProviderClient(),
355 GetMountId(), file_path, std::move(reply));
356
357 return EnqueueTaskAndGetCallback(std::move(task));
358 }
359
DeleteEntry(const base::FilePath & entry_path,bool recursive,storage::AsyncFileUtil::StatusCallback callback)360 AbortCallback SmbFileSystem::DeleteEntry(
361 const base::FilePath& entry_path,
362 bool recursive,
363 storage::AsyncFileUtil::StatusCallback callback) {
364 OperationId operation_id = task_queue_.GetNextOperationId();
365 SmbTask task;
366
367 if (recursive) {
368 auto reply = base::BindOnce(&SmbFileSystem::HandleGetDeleteListCallback,
369 AsWeakPtr(), std::move(callback), operation_id);
370 task = base::BindOnce(&SmbProviderClient::GetDeleteList,
371 GetWeakSmbProviderClient(), GetMountId(), entry_path,
372 std::move(reply));
373 } else {
374 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback,
375 AsWeakPtr(), std::move(callback));
376 task = base::BindOnce(&SmbProviderClient::DeleteEntry,
377 GetWeakSmbProviderClient(), GetMountId(), entry_path,
378 false /* recursive */, std::move(reply));
379 }
380
381 EnqueueTask(std::move(task), operation_id);
382 return CreateAbortCallback(operation_id);
383 }
384
CopyEntry(const base::FilePath & source_path,const base::FilePath & target_path,storage::AsyncFileUtil::StatusCallback callback)385 AbortCallback SmbFileSystem::CopyEntry(
386 const base::FilePath& source_path,
387 const base::FilePath& target_path,
388 storage::AsyncFileUtil::StatusCallback callback) {
389 OperationId operation_id = task_queue_.GetNextOperationId();
390
391 StartCopy(source_path, target_path, operation_id, std::move(callback));
392
393 return CreateAbortCallback(operation_id);
394 }
395
MoveEntry(const base::FilePath & source_path,const base::FilePath & target_path,storage::AsyncFileUtil::StatusCallback callback)396 AbortCallback SmbFileSystem::MoveEntry(
397 const base::FilePath& source_path,
398 const base::FilePath& target_path,
399 storage::AsyncFileUtil::StatusCallback callback) {
400 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
401 std::move(callback));
402 SmbTask task =
403 base::BindOnce(&SmbProviderClient::MoveEntry, GetWeakSmbProviderClient(),
404 GetMountId(), source_path, target_path, std::move(reply));
405
406 return EnqueueTaskAndGetCallback(std::move(task));
407 }
408
Truncate(const base::FilePath & file_path,int64_t length,storage::AsyncFileUtil::StatusCallback callback)409 AbortCallback SmbFileSystem::Truncate(
410 const base::FilePath& file_path,
411 int64_t length,
412 storage::AsyncFileUtil::StatusCallback callback) {
413 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
414 std::move(callback));
415 SmbTask task =
416 base::BindOnce(&SmbProviderClient::Truncate, GetWeakSmbProviderClient(),
417 GetMountId(), file_path, length, std::move(reply));
418
419 return EnqueueTaskAndGetCallback(std::move(task));
420 }
421
WriteFile(int file_handle,net::IOBuffer * buffer,int64_t offset,int length,storage::AsyncFileUtil::StatusCallback callback)422 AbortCallback SmbFileSystem::WriteFile(
423 int file_handle,
424 net::IOBuffer* buffer,
425 int64_t offset,
426 int length,
427 storage::AsyncFileUtil::StatusCallback callback) {
428 if (length == 0) {
429 std::move(callback).Run(base::File::FILE_OK);
430 return CreateAbortCallback();
431 }
432
433 const std::vector<uint8_t> data(buffer->data(), buffer->data() + length);
434 if (!temp_file_manager_) {
435 SmbTask task = base::BindOnce(
436 base::IgnoreResult(&SmbFileSystem::CallWriteFile), AsWeakPtr(),
437 file_handle, std::move(data), offset, length, std::move(callback));
438 CreateTempFileManagerAndExecuteTask(std::move(task));
439 // The call to init temp_file_manager_ will not be abortable since it is
440 // asynchronous.
441 return CreateAbortCallback();
442 }
443
444 return CallWriteFile(file_handle, data, offset, length, std::move(callback));
445 }
446
CreateTempFileManagerAndExecuteTask(SmbTask task)447 void SmbFileSystem::CreateTempFileManagerAndExecuteTask(SmbTask task) {
448 // CreateTempFileManager() has to be called on a separate thread since it
449 // contains a call that requires a blockable thread.
450 constexpr base::TaskTraits kTaskTraits = {
451 base::MayBlock(), base::TaskPriority::USER_BLOCKING,
452 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
453 auto init_task = base::BindOnce(&CreateTempFileManager);
454 auto reply = base::BindOnce(&SmbFileSystem::InitTempFileManagerAndExecuteTask,
455 AsWeakPtr(), std::move(task));
456 base::ThreadPool::PostTaskAndReplyWithResult(
457 FROM_HERE, kTaskTraits, std::move(init_task), std::move(reply));
458 }
459
InitTempFileManagerAndExecuteTask(SmbTask task,std::unique_ptr<TempFileManager> temp_file_manager)460 void SmbFileSystem::InitTempFileManagerAndExecuteTask(
461 SmbTask task,
462 std::unique_ptr<TempFileManager> temp_file_manager) {
463 DCHECK(!temp_file_manager_);
464 DCHECK(temp_file_manager);
465
466 temp_file_manager_ = std::move(temp_file_manager);
467 std::move(task).Run();
468 }
469
CallWriteFile(int file_handle,const std::vector<uint8_t> & data,int64_t offset,int length,storage::AsyncFileUtil::StatusCallback callback)470 AbortCallback SmbFileSystem::CallWriteFile(
471 int file_handle,
472 const std::vector<uint8_t>& data,
473 int64_t offset,
474 int length,
475 storage::AsyncFileUtil::StatusCallback callback) {
476 base::ScopedFD temp_fd = temp_file_manager_->CreateTempFile(data);
477
478 auto reply = base::BindOnce(&SmbFileSystem::HandleStatusCallback, AsWeakPtr(),
479 std::move(callback));
480 SmbTask task = base::BindOnce(
481 &SmbProviderClient::WriteFile, GetWeakSmbProviderClient(), GetMountId(),
482 file_handle, offset, length, std::move(temp_fd), std::move(reply));
483 return EnqueueTaskAndGetCallback(std::move(task));
484 }
485
AddWatcher(const GURL & origin,const base::FilePath & entry_path,bool recursive,bool persistent,storage::AsyncFileUtil::StatusCallback callback,storage::WatcherManager::NotificationCallback notification_callback)486 AbortCallback SmbFileSystem::AddWatcher(
487 const GURL& origin,
488 const base::FilePath& entry_path,
489 bool recursive,
490 bool persistent,
491 storage::AsyncFileUtil::StatusCallback callback,
492 storage::WatcherManager::NotificationCallback notification_callback) {
493 // Watchers are not supported.
494 NOTREACHED();
495 std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
496 return CreateAbortCallback();
497 }
498
RemoveWatcher(const GURL & origin,const base::FilePath & entry_path,bool recursive,storage::AsyncFileUtil::StatusCallback callback)499 void SmbFileSystem::RemoveWatcher(
500 const GURL& origin,
501 const base::FilePath& entry_path,
502 bool recursive,
503 storage::AsyncFileUtil::StatusCallback callback) {
504 // Watchers are not supported.
505 NOTREACHED();
506 std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
507 }
508
509 const file_system_provider::ProvidedFileSystemInfo&
GetFileSystemInfo() const510 SmbFileSystem::GetFileSystemInfo() const {
511 return file_system_info_;
512 }
513
GetRequestManager()514 file_system_provider::RequestManager* SmbFileSystem::GetRequestManager() {
515 NOTREACHED();
516 return NULL;
517 }
518
GetWatchers()519 file_system_provider::Watchers* SmbFileSystem::GetWatchers() {
520 // Watchers are not supported.
521 NOTREACHED();
522 return nullptr;
523 }
524
GetOpenedFiles() const525 const file_system_provider::OpenedFiles& SmbFileSystem::GetOpenedFiles() const {
526 NOTREACHED();
527 return opened_files_;
528 }
529
AddObserver(file_system_provider::ProvidedFileSystemObserver * observer)530 void SmbFileSystem::AddObserver(
531 file_system_provider::ProvidedFileSystemObserver* observer) {
532 NOTREACHED();
533 }
534
RemoveObserver(file_system_provider::ProvidedFileSystemObserver * observer)535 void SmbFileSystem::RemoveObserver(
536 file_system_provider::ProvidedFileSystemObserver* observer) {
537 NOTREACHED();
538 }
539
Notify(const base::FilePath & entry_path,bool recursive,storage::WatcherManager::ChangeType change_type,std::unique_ptr<file_system_provider::ProvidedFileSystemObserver::Changes> changes,const std::string & tag,storage::AsyncFileUtil::StatusCallback callback)540 void SmbFileSystem::SmbFileSystem::Notify(
541 const base::FilePath& entry_path,
542 bool recursive,
543 storage::WatcherManager::ChangeType change_type,
544 std::unique_ptr<file_system_provider::ProvidedFileSystemObserver::Changes>
545 changes,
546 const std::string& tag,
547 storage::AsyncFileUtil::StatusCallback callback) {
548 NOTREACHED();
549 }
550
Configure(storage::AsyncFileUtil::StatusCallback callback)551 void SmbFileSystem::Configure(storage::AsyncFileUtil::StatusCallback callback) {
552 NOTREACHED();
553 }
554
StartCopy(const base::FilePath & source_path,const base::FilePath & target_path,OperationId operation_id,storage::AsyncFileUtil::StatusCallback callback)555 void SmbFileSystem::StartCopy(const base::FilePath& source_path,
556 const base::FilePath& target_path,
557 OperationId operation_id,
558 storage::AsyncFileUtil::StatusCallback callback) {
559 auto reply = base::BindOnce(&SmbFileSystem::HandleStartCopyCallback,
560 AsWeakPtr(), std::move(callback), operation_id);
561 SmbTask task =
562 base::BindOnce(&SmbProviderClient::StartCopy, GetWeakSmbProviderClient(),
563 GetMountId(), source_path, target_path, std::move(reply));
564
565 EnqueueTask(std::move(task), operation_id);
566 }
567
ContinueCopy(OperationId operation_id,int32_t copy_token,storage::AsyncFileUtil::StatusCallback callback)568 void SmbFileSystem::ContinueCopy(
569 OperationId operation_id,
570 int32_t copy_token,
571 storage::AsyncFileUtil::StatusCallback callback) {
572 auto reply =
573 base::BindOnce(&SmbFileSystem::HandleContinueCopyCallback, AsWeakPtr(),
574 std::move(callback), operation_id, copy_token);
575 SmbTask task = base::BindOnce(&SmbProviderClient::ContinueCopy,
576 GetWeakSmbProviderClient(), GetMountId(),
577 copy_token, std::move(reply));
578
579 EnqueueTask(std::move(task), operation_id);
580 }
581
StartReadDirectory(const base::FilePath & directory_path,OperationId operation_id,storage::AsyncFileUtil::ReadDirectoryCallback callback)582 void SmbFileSystem::StartReadDirectory(
583 const base::FilePath& directory_path,
584 OperationId operation_id,
585 storage::AsyncFileUtil::ReadDirectoryCallback callback) {
586 base::ElapsedTimer metrics_timer;
587
588 auto reply = base::BindOnce(&SmbFileSystem::HandleStartReadDirectoryCallback,
589 AsWeakPtr(), std::move(callback), operation_id,
590 directory_path, std::move(metrics_timer));
591
592 SmbTask task = base::BindOnce(&SmbProviderClient::StartReadDirectory,
593 GetWeakSmbProviderClient(), GetMountId(),
594 directory_path, std::move(reply));
595
596 EnqueueTask(std::move(task), operation_id);
597 }
598
ContinueReadDirectory(OperationId operation_id,int32_t read_dir_token,storage::AsyncFileUtil::ReadDirectoryCallback callback,int entries_count,base::ElapsedTimer metrics_timer)599 void SmbFileSystem::ContinueReadDirectory(
600 OperationId operation_id,
601 int32_t read_dir_token,
602 storage::AsyncFileUtil::ReadDirectoryCallback callback,
603 int entries_count,
604 base::ElapsedTimer metrics_timer) {
605 auto reply =
606 base::BindOnce(&SmbFileSystem::HandleContinueReadDirectoryCallback,
607 AsWeakPtr(), std::move(callback), operation_id,
608 read_dir_token, entries_count, std::move(metrics_timer));
609 SmbTask task = base::BindOnce(&SmbProviderClient::ContinueReadDirectory,
610 GetWeakSmbProviderClient(), GetMountId(),
611 read_dir_token, std::move(reply));
612
613 EnqueueTask(std::move(task), operation_id);
614 }
615
RequestUpdatedCredentials(base::OnceClosure reply)616 void SmbFileSystem::RequestUpdatedCredentials(base::OnceClosure reply) {
617 request_creds_callback_.Run(GetMountPath(), GetMountId(), std::move(reply));
618 }
619
RequestUpdatedSharePath(SmbService::StartReadDirIfSuccessfulCallback reply)620 void SmbFileSystem::RequestUpdatedSharePath(
621 SmbService::StartReadDirIfSuccessfulCallback reply) {
622 request_path_callback_.Run(GetMountPath(), GetMountId(), std::move(reply));
623 }
624
HandleRequestReadDirectoryCallback(storage::AsyncFileUtil::ReadDirectoryCallback callback,const base::ElapsedTimer & metrics_timer,smbprovider::ErrorType error,const smbprovider::DirectoryEntryListProto & entries) const625 void SmbFileSystem::HandleRequestReadDirectoryCallback(
626 storage::AsyncFileUtil::ReadDirectoryCallback callback,
627 const base::ElapsedTimer& metrics_timer,
628 smbprovider::ErrorType error,
629 const smbprovider::DirectoryEntryListProto& entries) const {
630 task_queue_.TaskFinished();
631 uint32_t batch_size = kReadDirectoryInitialBatchSize;
632 storage::AsyncFileUtil::EntryList entry_list;
633
634 // Loop through the entries and send when the desired batch size is hit.
635 for (const smbprovider::DirectoryEntryProto& entry : entries.entries()) {
636 entry_list.emplace_back(base::FilePath(entry.name()),
637 MapEntryType(entry.is_directory()));
638
639 if (entry_list.size() == batch_size) {
640 callback.Run(base::File::FILE_OK, entry_list, true /* has_more */);
641 entry_list.clear();
642
643 // Double the batch size until it gets to the maximum size.
644 batch_size = std::min(batch_size * 2, kReadDirectoryMaxBatchSize);
645 }
646 }
647
648 callback.Run(TranslateToFileError(error), entry_list, false /* has_more */);
649 }
650
HandleGetDeleteListCallback(storage::AsyncFileUtil::StatusCallback callback,OperationId operation_id,smbprovider::ErrorType list_error,const smbprovider::DeleteListProto & delete_list)651 void SmbFileSystem::HandleGetDeleteListCallback(
652 storage::AsyncFileUtil::StatusCallback callback,
653 OperationId operation_id,
654 smbprovider::ErrorType list_error,
655 const smbprovider::DeleteListProto& delete_list) {
656 task_queue_.TaskFinished();
657 if (delete_list.entries_size() == 0) {
658 // There are no entries to delete.
659 DCHECK_NE(smbprovider::ERROR_OK, list_error);
660 std::move(callback).Run(TranslateToFileError(list_error));
661 return;
662 }
663
664 auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
665 for (int i = 0; i < delete_list.entries_size(); ++i) {
666 const base::FilePath entry_path(delete_list.entries(i));
667 bool is_last_entry = (i == delete_list.entries_size() - 1);
668
669 auto reply =
670 base::BindOnce(&SmbFileSystem::HandleDeleteEntryCallback, AsWeakPtr(),
671 copyable_callback, list_error, is_last_entry);
672
673 SmbTask task = base::BindOnce(
674 &SmbProviderClient::DeleteEntry, GetWeakSmbProviderClient(),
675 GetMountId(), entry_path, false /* recursive */, std::move(reply));
676 EnqueueTask(std::move(task), operation_id);
677 }
678 }
679
HandleDeleteEntryCallback(storage::AsyncFileUtil::StatusCallback callback,smbprovider::ErrorType list_error,bool is_last_entry,smbprovider::ErrorType delete_error) const680 void SmbFileSystem::HandleDeleteEntryCallback(
681 storage::AsyncFileUtil::StatusCallback callback,
682 smbprovider::ErrorType list_error,
683 bool is_last_entry,
684 smbprovider::ErrorType delete_error) const {
685 task_queue_.TaskFinished();
686 if (is_last_entry) {
687 // Only run the callback once.
688 if (list_error != smbprovider::ERROR_OK) {
689 delete_error = list_error;
690 }
691 std::move(callback).Run(TranslateToFileError(delete_error));
692 }
693 }
694
HandleStartCopyCallback(storage::AsyncFileUtil::StatusCallback callback,OperationId operation_id,smbprovider::ErrorType error,int32_t copy_token)695 void SmbFileSystem::HandleStartCopyCallback(
696 storage::AsyncFileUtil::StatusCallback callback,
697 OperationId operation_id,
698 smbprovider::ErrorType error,
699 int32_t copy_token) {
700 task_queue_.TaskFinished();
701
702 if (error == smbprovider::ERROR_COPY_PENDING) {
703 // The copy needs to be continued.
704 DCHECK_GE(copy_token, 0);
705
706 ContinueCopy(operation_id, copy_token, std::move(callback));
707 return;
708 }
709
710 std::move(callback).Run(TranslateToFileError(error));
711 }
712
HandleContinueCopyCallback(storage::AsyncFileUtil::StatusCallback callback,OperationId operation_id,int32_t copy_token,smbprovider::ErrorType error)713 void SmbFileSystem::HandleContinueCopyCallback(
714 storage::AsyncFileUtil::StatusCallback callback,
715 OperationId operation_id,
716 int32_t copy_token,
717 smbprovider::ErrorType error) {
718 task_queue_.TaskFinished();
719
720 if (error == smbprovider::ERROR_COPY_PENDING) {
721 // The copy needs to be continued.
722 ContinueCopy(operation_id, copy_token, std::move(callback));
723 return;
724 }
725
726 std::move(callback).Run(TranslateToFileError(error));
727 }
728
HandleStartReadDirectoryCallback(storage::AsyncFileUtil::ReadDirectoryCallback callback,OperationId operation_id,const base::FilePath & directory_path,base::ElapsedTimer metrics_timer,smbprovider::ErrorType error,int32_t read_dir_token,const smbprovider::DirectoryEntryListProto & entries)729 void SmbFileSystem::HandleStartReadDirectoryCallback(
730 storage::AsyncFileUtil::ReadDirectoryCallback callback,
731 OperationId operation_id,
732 const base::FilePath& directory_path,
733 base::ElapsedTimer metrics_timer,
734 smbprovider::ErrorType error,
735 int32_t read_dir_token,
736 const smbprovider::DirectoryEntryListProto& entries) {
737 task_queue_.TaskFinished();
738
739 if (IsRecoverableError(error)) {
740 if (error == smbprovider::ERROR_ACCESS_DENIED) {
741 base::OnceClosure retry =
742 base::BindOnce(&SmbFileSystem::StartReadDirectory, AsWeakPtr(),
743 directory_path, operation_id, std::move(callback));
744 // Request updated credentials for share, then retry the read directory
745 // from the start.
746 RequestUpdatedCredentials(std::move(retry));
747 return;
748 }
749
750 if (error == smbprovider::ERROR_NOT_FOUND) {
751 // Request updated share path for share, then retry the read directory
752 // from the start.
753 SmbService::StartReadDirIfSuccessfulCallback retry_start_read_dir =
754 base::BindOnce(&SmbFileSystem::RetryStartReadDir, AsWeakPtr(),
755 directory_path, operation_id, std::move(callback));
756 RequestUpdatedSharePath(std::move(retry_start_read_dir));
757 return;
758 }
759 }
760
761 int entries_count = 0;
762 ProcessReadDirectoryResults(std::move(callback), operation_id, read_dir_token,
763 error, entries, entries_count,
764 std::move(metrics_timer));
765 }
766
HandleContinueReadDirectoryCallback(storage::AsyncFileUtil::ReadDirectoryCallback callback,OperationId operation_id,int32_t read_dir_token,int entries_count,base::ElapsedTimer metrics_timer,smbprovider::ErrorType error,const smbprovider::DirectoryEntryListProto & entries)767 void SmbFileSystem::HandleContinueReadDirectoryCallback(
768 storage::AsyncFileUtil::ReadDirectoryCallback callback,
769 OperationId operation_id,
770 int32_t read_dir_token,
771 int entries_count,
772 base::ElapsedTimer metrics_timer,
773 smbprovider::ErrorType error,
774 const smbprovider::DirectoryEntryListProto& entries) {
775 task_queue_.TaskFinished();
776
777 ProcessReadDirectoryResults(std::move(callback), operation_id, read_dir_token,
778 error, entries, entries_count,
779 std::move(metrics_timer));
780 }
781
ProcessReadDirectoryResults(storage::AsyncFileUtil::ReadDirectoryCallback callback,OperationId operation_id,int32_t read_dir_token,smbprovider::ErrorType error,const smbprovider::DirectoryEntryListProto & entries,int entries_count,base::ElapsedTimer metrics_timer)782 void SmbFileSystem::ProcessReadDirectoryResults(
783 storage::AsyncFileUtil::ReadDirectoryCallback callback,
784 OperationId operation_id,
785 int32_t read_dir_token,
786 smbprovider::ErrorType error,
787 const smbprovider::DirectoryEntryListProto& entries,
788 int entries_count,
789 base::ElapsedTimer metrics_timer) {
790 storage::AsyncFileUtil::EntryList entry_list;
791
792 if (error != smbprovider::ERROR_OPERATION_PENDING &&
793 error != smbprovider::ERROR_OK) {
794 callback.Run(TranslateToFileError(error), entry_list, false /* has_more */);
795 }
796
797 ConvertEntries(entries, &entry_list);
798 entries_count += entry_list.size();
799
800 const bool has_more = (error == smbprovider::ERROR_OPERATION_PENDING);
801
802 // Run |callback| with the next batch of results;
803 callback.Run(base::File::FILE_OK, entry_list, has_more);
804
805 if (has_more) {
806 // There are more directory entries to read.
807 ContinueReadDirectory(operation_id, read_dir_token, callback, entries_count,
808 std::move(metrics_timer));
809 return;
810 }
811 }
812
HandleSyncRedundantGetMetadata(ProvidedFileSystemInterface::MetadataFieldMask fields,ProvidedFileSystemInterface::GetMetadataCallback callback)813 AbortCallback SmbFileSystem::HandleSyncRedundantGetMetadata(
814 ProvidedFileSystemInterface::MetadataFieldMask fields,
815 ProvidedFileSystemInterface::GetMetadataCallback callback) {
816 auto metadata = std::make_unique<file_system_provider::EntryMetadata>();
817
818 // The fields could be empty or have one or both of thumbnail and metadata.
819 // We completely ignore metadata, but populate the bogus URI for the
820 // thumbnail.
821 if (RequestedThumbnail(fields)) {
822 metadata->thumbnail = std::make_unique<std::string>(kUnknownImageDataUri);
823 }
824
825 std::move(callback).Run(std::move(metadata), base::File::FILE_OK);
826 return CreateAbortCallback();
827 }
828
HandleRequestGetMetadataEntryCallback(ProvidedFileSystemInterface::MetadataFieldMask fields,ProvidedFileSystemInterface::GetMetadataCallback callback,smbprovider::ErrorType error,const smbprovider::DirectoryEntryProto & entry) const829 void SmbFileSystem::HandleRequestGetMetadataEntryCallback(
830 ProvidedFileSystemInterface::MetadataFieldMask fields,
831 ProvidedFileSystemInterface::GetMetadataCallback callback,
832 smbprovider::ErrorType error,
833 const smbprovider::DirectoryEntryProto& entry) const {
834 task_queue_.TaskFinished();
835 if (error != smbprovider::ERROR_OK) {
836 std::move(callback).Run(nullptr, TranslateToFileError(error));
837 return;
838 }
839 std::unique_ptr<file_system_provider::EntryMetadata> metadata =
840 std::make_unique<file_system_provider::EntryMetadata>();
841 if (RequestedIsDirectory(fields)) {
842 metadata->is_directory = std::make_unique<bool>(entry.is_directory());
843 }
844 if (RequestedName(fields)) {
845 metadata->name = std::make_unique<std::string>(entry.name());
846 }
847 if (RequestedSize(fields)) {
848 metadata->size = std::make_unique<int64_t>(entry.size());
849 }
850 if (RequestedModificationTime(fields)) {
851 metadata->modification_time = std::make_unique<base::Time>(
852 base::Time::FromTimeT(entry.last_modified_time()));
853 }
854 if (RequestedThumbnail(fields)) {
855 metadata->thumbnail = std::make_unique<std::string>(kUnknownImageDataUri);
856 }
857 // Mime types are not supported.
858 std::move(callback).Run(std::move(metadata), base::File::FILE_OK);
859 }
860
RunUnmountCallback(const std::string & file_system_id,file_system_provider::Service::UnmountReason reason)861 base::File::Error SmbFileSystem::RunUnmountCallback(
862 const std::string& file_system_id,
863 file_system_provider::Service::UnmountReason reason) {
864 base::File::Error error =
865 std::move(unmount_callback_).Run(file_system_id, reason);
866 return error;
867 }
868
HandleRequestReadFileCallback(int32_t length,scoped_refptr<net::IOBuffer> buffer,ReadChunkReceivedCallback callback,smbprovider::ErrorType error,const base::ScopedFD & fd) const869 void SmbFileSystem::HandleRequestReadFileCallback(
870 int32_t length,
871 scoped_refptr<net::IOBuffer> buffer,
872 ReadChunkReceivedCallback callback,
873 smbprovider::ErrorType error,
874 const base::ScopedFD& fd) const {
875 task_queue_.TaskFinished();
876
877 if (error != smbprovider::ERROR_OK) {
878 callback.Run(0 /* chunk_length */, false /* has_more */,
879 TranslateToFileError(error));
880 return;
881 }
882
883 int32_t total_read = 0;
884 while (total_read < length) {
885 // read() may return less than the requested amount of bytes. If this
886 // happens, try to read the remaining bytes.
887 const int32_t bytes_read = HANDLE_EINTR(
888 read(fd.get(), buffer->data() + total_read, length - total_read));
889 if (bytes_read < 0) {
890 // This is an error case, return an error immediately.
891 callback.Run(0 /* chunk_length */, false /* has_more */,
892 base::File::FILE_ERROR_IO);
893 return;
894 }
895 if (bytes_read == 0) {
896 break;
897 }
898 total_read += bytes_read;
899 }
900 callback.Run(total_read, false /* has_more */, base::File::FILE_OK);
901 }
902
HandleStatusCallback(storage::AsyncFileUtil::StatusCallback callback,smbprovider::ErrorType error) const903 void SmbFileSystem::HandleStatusCallback(
904 storage::AsyncFileUtil::StatusCallback callback,
905 smbprovider::ErrorType error) const {
906 task_queue_.TaskFinished();
907
908 std::move(callback).Run(TranslateToFileError(error));
909 }
910
911 base::WeakPtr<file_system_provider::ProvidedFileSystemInterface>
GetWeakPtr()912 SmbFileSystem::GetWeakPtr() {
913 return AsWeakPtr();
914 }
915
IsRecoverableError(smbprovider::ErrorType error) const916 bool SmbFileSystem::IsRecoverableError(smbprovider::ErrorType error) const {
917 return (error == smbprovider::ERROR_NOT_FOUND) ||
918 (error == smbprovider::ERROR_INVALID_OPERATION) ||
919 (error == smbprovider::ERROR_ACCESS_DENIED);
920 }
921
RetryStartReadDir(const base::FilePath & directory_path,OperationId operation_id,storage::AsyncFileUtil::ReadDirectoryCallback callback,bool should_retry_start_read_dir)922 void SmbFileSystem::RetryStartReadDir(
923 const base::FilePath& directory_path,
924 OperationId operation_id,
925 storage::AsyncFileUtil::ReadDirectoryCallback callback,
926 bool should_retry_start_read_dir) {
927 if (should_retry_start_read_dir) {
928 StartReadDirectory(directory_path, operation_id, std::move(callback));
929 } else {
930 // Run |callback| to terminate StartReadDirectory early.
931 std::move(callback).Run(base::File::FILE_ERROR_NOT_FOUND,
932 storage::AsyncFileUtil::EntryList(),
933 false /* has_more */);
934 }
935 }
936
937 } // namespace smb_client
938 } // namespace chromeos
939