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