fusebox: Add DirEntry stream response error handling

A FUSE readdir op will create DirEntryResponse to collect responses
(DirEntry list chunks, streamed from the server) and append them to
DirEntryResponse DirEntry list.

Any received chunk can have a non-zero error. Add an "Append" error
API to DirEntryResponse. Like other Append API, Respond() to Kernel
Fuse DirEntryRequest(s) [1].

[1] One error is sufficient in practice to end the FUSE readdir op.
The Kernel sends a FUSE releasedir op to close the dir on receiving
a readdir op error.

BUG=chromium:1244007
TEST=cros_workon_make --board=${BOARD} fusebox --test

Change-Id: If80bb95cb9a7c268157a440d03e3b9fba3baa1eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/3230449
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Commit-Queue: Noel Gordon <noel@chromium.org>
Tested-by: Noel Gordon <noel@chromium.org>
diff --git a/fusebox/fuse_request.cc b/fusebox/fuse_request.cc
index 8c9c259..59f0934 100644
--- a/fusebox/fuse_request.cc
+++ b/fusebox/fuse_request.cc
@@ -144,12 +144,20 @@
   Respond();
 }
 
+void DirEntryResponse::Append(int error) {
+  error_ = error;
+  Respond();
+}
+
 void DirEntryResponse::Respond() {
   const auto process_next_request = [&]() {
     DCHECK(!request_.empty());
 
-    if (request_[0]->IsInterrupted()) {
-      request_.erase(request_.begin());
+    if (request_[0]->IsInterrupted())
+      return true;
+
+    if (error_) {
+      request_[0]->ReplyError(error_);
       return true;
     }
 
@@ -161,14 +169,12 @@
       if (request_[0]->AddEntry(entry_[offset++], next))
         continue;  // add next entry
       request_[0]->ReplyDone();
-      request_.erase(request_.begin());
       return true;
     }
 
     CHECK_GE(offset, entry_.size());
     if (end_) {
       request_[0]->ReplyDone();
-      request_.erase(request_.begin());
       return true;
     }
 
@@ -178,6 +184,7 @@
   while (!request_.empty() && !entry_.empty()) {
     if (!process_next_request())
       break;
+    request_.erase(request_.begin());
   }
 }
 
diff --git a/fusebox/fuse_request.h b/fusebox/fuse_request.h
index 638e051..4721b26 100644
--- a/fusebox/fuse_request.h
+++ b/fusebox/fuse_request.h
@@ -139,6 +139,9 @@
   // Append |request| to the DirEntryRequest list.
   void Append(std::unique_ptr<DirEntryRequest> request);
 
+  // Error the DirEntry list with errno |error|.
+  void Append(int error);
+
  private:
   // Called on Append() to respond to DirEntry requests.
   void Respond();
@@ -153,6 +156,9 @@
   // List of DirEntry from the file system: readdir(2).
   std::vector<struct DirEntry> entry_;
 
+  // Error state of the DirEntry list.
+  int error_ = 0;
+
   // True when the DirEntry list is complete.
   bool end_ = false;
 };