blob: 9724b9f31040822f55476209d26e7246c8673c00 [file] [log] [blame]
// 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.
#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 {
using CompletionCallback =
// |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();
// Helper for unit tests.
void SetSambaInterface(SambaInterface* samba_impl);
// 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_ =
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_ =
base::WeakPtrFactory<RecursiveDeleteOperation> weak_factory_{this};
} // namespace smbfs