recursive_operation_delegate.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright (c) 2013 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. #include "storage/browser/file_system/recursive_operation_delegate.h"
  5. #include <stddef.h>
  6. #include "base/bind.h"
  7. #include "base/check_op.h"
  8. #include "base/containers/queue.h"
  9. #include "base/files/file.h"
  10. #include "base/task/single_thread_task_runner.h"
  11. #include "base/threading/thread_task_runner_handle.h"
  12. #include "storage/browser/file_system/file_system_context.h"
  13. #include "storage/browser/file_system/file_system_operation_runner.h"
  14. namespace storage {
  15. RecursiveOperationDelegate::RecursiveOperationDelegate(
  16. FileSystemContext* file_system_context)
  17. : file_system_context_(file_system_context) {}
  18. RecursiveOperationDelegate::~RecursiveOperationDelegate() = default;
  19. void RecursiveOperationDelegate::Cancel() {
  20. canceled_ = true;
  21. OnCancel();
  22. }
  23. void RecursiveOperationDelegate::StartRecursiveOperation(
  24. const FileSystemURL& root,
  25. ErrorBehavior error_behavior,
  26. StatusCallback callback) {
  27. DCHECK(pending_directory_stack_.empty());
  28. DCHECK(pending_files_.empty());
  29. error_behavior_ = error_behavior;
  30. callback_ = std::move(callback);
  31. TryProcessFile(root);
  32. }
  33. void RecursiveOperationDelegate::TryProcessFile(const FileSystemURL& root) {
  34. ProcessFile(root,
  35. base::BindOnce(&RecursiveOperationDelegate::DidTryProcessFile,
  36. AsWeakPtr(), root));
  37. }
  38. FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
  39. return file_system_context_->operation_runner();
  40. }
  41. void RecursiveOperationDelegate::OnCancel() {}
  42. void RecursiveOperationDelegate::DidTryProcessFile(const FileSystemURL& root,
  43. base::File::Error error) {
  44. DCHECK(pending_directory_stack_.empty());
  45. DCHECK(pending_files_.empty());
  46. if (canceled_ || error != base::File::FILE_ERROR_NOT_A_FILE) {
  47. Done(error);
  48. return;
  49. }
  50. pending_directory_stack_.push(base::queue<FileSystemURL>());
  51. pending_directory_stack_.top().push(root);
  52. ProcessNextDirectory();
  53. }
  54. void RecursiveOperationDelegate::ProcessNextDirectory() {
  55. DCHECK(pending_files_.empty());
  56. DCHECK(!pending_directory_stack_.empty());
  57. DCHECK(!pending_directory_stack_.top().empty());
  58. const FileSystemURL& url = pending_directory_stack_.top().front();
  59. ProcessDirectory(
  60. url, base::BindOnce(&RecursiveOperationDelegate::DidProcessDirectory,
  61. AsWeakPtr()));
  62. }
  63. void RecursiveOperationDelegate::DidProcessDirectory(base::File::Error error) {
  64. DCHECK(pending_files_.empty());
  65. DCHECK(!pending_directory_stack_.empty());
  66. DCHECK(!pending_directory_stack_.top().empty());
  67. if (canceled_ || error != base::File::FILE_OK) {
  68. if (canceled_ ||
  69. error_behavior_ == FileSystemOperation::ERROR_BEHAVIOR_ABORT) {
  70. Done(error);
  71. return;
  72. }
  73. SetPreviousError(error);
  74. // For ERROR_BEHAVIOR_SKIP, we skip processing the current directory and
  75. // proceed with the next.
  76. pending_directory_stack_.top().pop();
  77. ProcessSubDirectory();
  78. return;
  79. }
  80. const FileSystemURL& parent = pending_directory_stack_.top().front();
  81. pending_directory_stack_.push(base::queue<FileSystemURL>());
  82. operation_runner()->ReadDirectory(
  83. parent, base::BindRepeating(&RecursiveOperationDelegate::DidReadDirectory,
  84. AsWeakPtr(), parent));
  85. }
  86. void RecursiveOperationDelegate::DidReadDirectory(const FileSystemURL& parent,
  87. base::File::Error error,
  88. FileEntryList entries,
  89. bool has_more) {
  90. DCHECK(!pending_directory_stack_.empty());
  91. if (canceled_ || error != base::File::FILE_OK) {
  92. Done(error);
  93. return;
  94. }
  95. for (size_t i = 0; i < entries.size(); i++) {
  96. FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
  97. parent.storage_key(), parent.mount_type(),
  98. parent.virtual_path().Append(entries[i].name));
  99. if (parent.bucket().has_value())
  100. url.SetBucket(parent.bucket().value());
  101. if (entries[i].type == filesystem::mojom::FsFileType::DIRECTORY)
  102. pending_directory_stack_.top().push(url);
  103. else
  104. pending_files_.push(url);
  105. }
  106. // Wait for next entries.
  107. if (has_more)
  108. return;
  109. ProcessPendingFiles();
  110. }
  111. void RecursiveOperationDelegate::ProcessPendingFiles() {
  112. DCHECK(!pending_directory_stack_.empty());
  113. if (pending_files_.empty() || canceled_) {
  114. ProcessSubDirectory();
  115. return;
  116. }
  117. // Do not post any new tasks.
  118. if (canceled_)
  119. return;
  120. // Run ProcessFile.
  121. scoped_refptr<base::SingleThreadTaskRunner> current_task_runner =
  122. base::ThreadTaskRunnerHandle::Get();
  123. if (!pending_files_.empty()) {
  124. current_task_runner->PostTask(
  125. FROM_HERE,
  126. base::BindOnce(
  127. &RecursiveOperationDelegate::ProcessFile, AsWeakPtr(),
  128. pending_files_.front(),
  129. base::BindOnce(&RecursiveOperationDelegate::DidProcessFile,
  130. AsWeakPtr(), pending_files_.front())));
  131. pending_files_.pop();
  132. }
  133. }
  134. void RecursiveOperationDelegate::DidProcessFile(const FileSystemURL& url,
  135. base::File::Error error) {
  136. if (error != base::File::FILE_OK) {
  137. if (error_behavior_ == FileSystemOperation::ERROR_BEHAVIOR_ABORT) {
  138. // If an error occurs, invoke Done immediately (even if there remain
  139. // running operations). It is because in the callback, this instance is
  140. // deleted.
  141. Done(error);
  142. return;
  143. }
  144. SetPreviousError(error);
  145. }
  146. ProcessPendingFiles();
  147. }
  148. void RecursiveOperationDelegate::ProcessSubDirectory() {
  149. DCHECK(pending_files_.empty());
  150. DCHECK(!pending_directory_stack_.empty());
  151. if (canceled_) {
  152. Done(base::File::FILE_ERROR_ABORT);
  153. return;
  154. }
  155. if (!pending_directory_stack_.top().empty()) {
  156. // There remain some sub directories. Process them first.
  157. ProcessNextDirectory();
  158. return;
  159. }
  160. // All subdirectories are processed.
  161. pending_directory_stack_.pop();
  162. if (pending_directory_stack_.empty()) {
  163. // All files/directories are processed.
  164. Done(base::File::FILE_OK);
  165. return;
  166. }
  167. DCHECK(!pending_directory_stack_.top().empty());
  168. PostProcessDirectory(
  169. pending_directory_stack_.top().front(),
  170. base::BindOnce(&RecursiveOperationDelegate::DidPostProcessDirectory,
  171. AsWeakPtr()));
  172. }
  173. void RecursiveOperationDelegate::DidPostProcessDirectory(
  174. base::File::Error error) {
  175. DCHECK(pending_files_.empty());
  176. DCHECK(!pending_directory_stack_.empty());
  177. DCHECK(!pending_directory_stack_.top().empty());
  178. pending_directory_stack_.top().pop();
  179. if (canceled_ || error != base::File::FILE_OK) {
  180. if (canceled_ || error_behavior_ == ErrorBehavior::ERROR_BEHAVIOR_ABORT) {
  181. Done(error);
  182. return;
  183. }
  184. SetPreviousError(error);
  185. }
  186. ProcessSubDirectory();
  187. }
  188. void RecursiveOperationDelegate::SetPreviousError(base::File::Error error) {
  189. DCHECK_NE(error, base::File::FILE_OK);
  190. previous_error_ = error;
  191. }
  192. void RecursiveOperationDelegate::Done(base::File::Error error) {
  193. if (canceled_ && error == base::File::FILE_OK) {
  194. std::move(callback_).Run(base::File::FILE_ERROR_ABORT);
  195. } else {
  196. if (error != base::File::FILE_OK) {
  197. std::move(callback_).Run(error);
  198. } else {
  199. std::move(callback_).Run(previous_error_);
  200. }
  201. }
  202. }
  203. } // namespace storage