Save uploaded files in multipart request to disk

Instead of keeping them in memory. Pass the file descriptor/file stream
to the handler process when asking for the uploaded file data.

BUG: 24166746
Change-Id: Ife4f5b4fa422d99272b15f6f02e4b3b515e4e3b4
diff --git a/libwebserv/protocol_handler.cc b/libwebserv/protocol_handler.cc
index e7e8dc8..544281f 100644
--- a/libwebserv/protocol_handler.cc
+++ b/libwebserv/protocol_handler.cc
@@ -18,6 +18,7 @@
 
 #include <base/logging.h>
 #include <chromeos/map_utils.h>
+#include <chromeos/streams/file_stream.h>
 
 #include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
 #include "libwebserv/request.h"
@@ -226,13 +227,41 @@
 void ProtocolHandler::GetFileData(
     const std::string& request_id,
     int file_id,
-    const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+    const base::Callback<void(chromeos::StreamPtr)>& success_callback,
     const base::Callback<void(chromeos::Error*)>& error_callback) {
   ProtocolHandlerProxy* proxy = GetRequestProtocolHandlerProxy(request_id);
   CHECK(proxy);
 
-  proxy->GetRequestFileDataAsync(
-      request_id, file_id, success_callback, error_callback);
+  // Store the success/error callback in a shared object so it can be referenced
+  // by the two wrapper callbacks. Since the original callbacks MAY contain
+  // move-only types, copying the base::Callback object is generally unsafe and
+  // may destroy the source object of the copy (despite the fact that it is
+  // constant). So, here we move both callbacks to |Callbacks| structure and
+  // use a shared pointer to it in both success and error callback wrappers.
+  struct Callbacks {
+    base::Callback<void(chromeos::StreamPtr)> on_success;
+    base::Callback<void(chromeos::Error*)> on_error;
+  };
+  auto callbacks = std::make_shared<Callbacks>();
+  callbacks->on_success = success_callback;
+  callbacks->on_error = error_callback;
+
+  auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
+    chromeos::ErrorPtr error;
+    // Unfortunately there is no way to take ownership of the file descriptor
+    // since |fd| is a const reference, so duplicate the descriptor.
+    int dupfd = dup(fd.value());
+    auto stream = chromeos::FileStream::FromFileDescriptor(dupfd, true, &error);
+    if (!stream)
+      return callbacks->on_error.Run(error.get());
+    callbacks->on_success.Run(std::move(stream));
+  };
+  auto on_error = [callbacks](chromeos::Error* error) {
+    callbacks->on_error.Run(error);
+  };
+
+  proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
+                                 base::Bind(on_error));
 }
 
 ProtocolHandler::ProtocolHandlerProxy*