| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SMBFS_RECURSIVE_DELETE_OPERATION_H_ |
| #define SMBFS_RECURSIVE_DELETE_OPERATION_H_ |
| |
| #include <libsmbclient.h> |
| |
| #include <list> |
| #include <string> |
| |
| #include <base/callback.h> |
| #include <base/files/file_path.h> |
| #include <base/memory/weak_ptr.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <gtest/gtest_prod.h> |
| |
| #include "smbfs/mojom/smbfs.mojom.h" |
| #include "smbfs/samba_interface.h" |
| |
| namespace smbfs { |
| |
| class SmbFilesystem; |
| |
| // Recursively deletes all nodes (files and sub-directories) under a given |
| // |root_path| by doing a post-order DFT of the directory tree and spreading |
| // I/O calls out over multiple tasks. |completion_callback| is called once the |
| // operation succeeds or fails. |
| // |
| // This class must be created, used and destroyed on the same thread. |
| class RecursiveDeleteOperation { |
| public: |
| using CompletionCallback = |
| base::OnceCallback<void(mojom::DeleteRecursivelyError)>; |
| |
| // |base_share_path| is a fully-qualified SMB share path *without* the |
| // trailing slash (ie. smb://server/share) and |root_path| should be the |
| // absolute path, within that share, to the directory being deleted (ie. |
| // /delete/this/dir). |
| RecursiveDeleteOperation(SambaInterface* samba_impl, |
| const std::string& base_share_path, |
| const base::FilePath& root_path, |
| CompletionCallback completion_callback); |
| |
| RecursiveDeleteOperation(const RecursiveDeleteOperation&) = delete; |
| RecursiveDeleteOperation& operator=(const RecursiveDeleteOperation&) = delete; |
| |
| // Start deletion from |root_path_|, which may be a file or directory. |
| void Start(); |
| |
| protected: |
| // Helper for unit tests. |
| void SetSambaInterface(SambaInterface* samba_impl); |
| |
| private: |
| // Multiple methods can serve as ContinuationCallbacks (ie. Finished(), |
| // ProcessDirectoryEntries()). The overall operation is aborted whenever the |
| // parameter to this callback is false. |
| using ContinuationCallback = base::OnceCallback<void(bool)>; |
| |
| FRIEND_TEST(RecursiveDeleteOperationTest, DeleteFile); |
| FRIEND_TEST(RecursiveDeleteOperationTest, DeleteDirectory); |
| FRIEND_TEST(RecursiveDeleteOperationTest, CloseDirectory); |
| FRIEND_TEST(RecursiveDeleteOperationTest, GetDirectoryListing); |
| |
| struct Entry { |
| base::FilePath path; |
| bool is_directory; |
| }; |
| |
| // The top-level ContinuationCallback that is called when the recursive |
| // deletion completes due to success or failure. It marshalls |last_error_| |
| // back to the |completion_callback_|. |
| void Finished(bool success); |
| |
| // Delete the directory |dir_path| recursively. |path_removed_callback| is |
| // called once |dir_path| is fully removed and may be Finished() (for the |
| // top-level directory) or ProcessDirectoryEntries() if continuing to process |
| // the siblings of a |dir_path| that is a descendant of |root_path_|. |
| void DeleteRecursively(const base::FilePath& dir_path, |
| ContinuationCallback path_removed_callback); |
| |
| // Process (traverse directories, delete files) all |entries| from the |
| // directory currently being deleted by DeleteRecursively(). For any given |
| // directory this method will be called multiple times, working its way |
| // through |entries| one-at-a-time. Each time it is called the previous entry |
| // is guaranteed to be completely removed unless |previous_entry_succeeded| is |
| // false (which aborts the entire process). |
| void ProcessDirectoryEntries( |
| std::list<Entry> entries, |
| ContinuationCallback processing_complete_callback, |
| bool previous_entry_succeeded); |
| |
| // Called once all entries from a directory have been processed. If they |
| // were all successfully removed (|all_descendants_removed| is true) it will |
| // proceed to remove the directory itself. The callback may be Finished() or a |
| // follow-up call to ProcessDirectoryEntries() to continue processing the list |
| // of siblings of an entry that was itself a directory. |
| void OnProcessDirectoryEntriesDone(const base::FilePath& dir_path, |
| ContinuationCallback path_removed_callback, |
| bool all_descendants_removed); |
| |
| // Descends into directories or removes a file. |
| void ProcessSingleDirectoryEntry(const Entry& entry, |
| ContinuationCallback entry_removed_callback); |
| |
| // Build a list of Entry's to describe the contents of directory |dir_path|. |
| bool GetDirectoryListing(const base::FilePath& dir_path, |
| std::list<Entry>* const entries); |
| |
| bool DeleteDirectory(const base::FilePath& dir_path); |
| bool DeleteFile(const base::FilePath& file_path); |
| bool CloseDirectory(SMBCFILE* dir); |
| std::string MakeSharePath(const base::FilePath& path); |
| |
| // Origin/constructor thread task runner. |
| scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_ = |
| base::ThreadTaskRunnerHandle::Get(); |
| SambaInterface* samba_impl_; |
| const std::string base_share_path_; |
| const base::FilePath root_path_; |
| CompletionCallback completion_callback_; |
| |
| // If progress is aborted at any time, the reason is stored in |last_error_| |
| // and marshalled back to |completion_callback_| via Finished(). |
| mojom::DeleteRecursivelyError last_error_ = |
| mojom::DeleteRecursivelyError::kOk; |
| |
| base::WeakPtrFactory<RecursiveDeleteOperation> weak_factory_{this}; |
| }; |
| |
| } // namespace smbfs |
| |
| #endif // SMBFS_RECURSIVE_DELETE_OPERATION_H_ |