Add FuseMessage::WriteWithBody function

The funciton is going to be used to write FUSE header with external
body.

Bug: 35229514
Test: libappfuse_tests
Change-Id: I303022b555deca960b8e08f26140a5ef10133efe
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 13cfc88..5bc5497 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -24,6 +24,7 @@
 #include <type_traits>
 
 #include <sys/socket.h>
+#include <sys/uio.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -34,9 +35,9 @@
 namespace {
 
 template <typename T>
-bool CheckHeaderLength(const FuseMessage<T>* self, const char* name) {
+bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
     const auto& header = static_cast<const T*>(self)->header;
-    if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
+    if (header.len >= sizeof(header) && header.len <= max_size) {
         return true;
     } else {
         LOG(ERROR) << "Invalid header length is found in " << name << ": " << header.len;
@@ -68,7 +69,7 @@
         return ResultOrAgain::kFailure;
     }
 
-    if (!CheckHeaderLength<T>(self, "Read")) {
+    if (!CheckHeaderLength<T>(self, "Read", sizeof(T))) {
         return ResultOrAgain::kFailure;
     }
 
@@ -81,15 +82,26 @@
 }
 
 template <typename T>
-ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag) {
-    if (!CheckHeaderLength<T>(self, "Write")) {
+ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, const void* data,
+                            size_t max_size) {
+    if (!CheckHeaderLength<T>(self, "Write", max_size)) {
         return ResultOrAgain::kFailure;
     }
 
     const char* const buf = reinterpret_cast<const char*>(self);
     const auto& header = static_cast<const T*>(self)->header;
-    const int result = sockflag ? TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag))
-                                : TEMP_FAILURE_RETRY(write(fd, buf, header.len));
+
+    int result;
+    if (sockflag) {
+        CHECK(data == nullptr);
+        result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
+    } else if (data) {
+        const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
+                                    {const_cast<void*>(data), header.len - sizeof(header)}};
+        result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
+    } else {
+        result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
+    }
 
     if (result == -1) {
         if (errno == EAGAIN) {
@@ -143,17 +155,20 @@
 
 template <typename T>
 bool FuseMessage<T>::Write(int fd) const {
-    return WriteInternal(this, fd, 0) == ResultOrAgain::kSuccess;
+    return WriteInternal(this, fd, 0, nullptr, sizeof(T)) == ResultOrAgain::kSuccess;
+}
+
+template <typename T>
+bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
+    CHECK(data != nullptr);
+    return WriteInternal<T>(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
 }
 
 template <typename T>
 ResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const {
-    return WriteInternal(this, fd, MSG_DONTWAIT);
+    return WriteInternal(this, fd, MSG_DONTWAIT, nullptr, sizeof(T));
 }
 
-template class FuseMessage<FuseRequest>;
-template class FuseMessage<FuseResponse>;
-
 void FuseRequest::Reset(
     uint32_t data_length, uint32_t opcode, uint64_t unique) {
   memset(this, 0, sizeof(fuse_in_header) + data_length);
@@ -162,17 +177,18 @@
   header.unique = unique;
 }
 
-void FuseResponse::ResetHeader(
-    uint32_t data_length, int32_t error, uint64_t unique) {
-  CHECK_LE(error, 0) << "error should be zero or negative.";
-  header.len = sizeof(fuse_out_header) + data_length;
-  header.error = error;
-  header.unique = unique;
+template <size_t N>
+void FuseResponseBase<N>::ResetHeader(uint32_t data_length, int32_t error, uint64_t unique) {
+    CHECK_LE(error, 0) << "error should be zero or negative.";
+    header.len = sizeof(fuse_out_header) + data_length;
+    header.error = error;
+    header.unique = unique;
 }
 
-void FuseResponse::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
-  memset(this, 0, sizeof(fuse_out_header) + data_length);
-  ResetHeader(data_length, error, unique);
+template <size_t N>
+void FuseResponseBase<N>::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
+    memset(this, 0, sizeof(fuse_out_header) + data_length);
+    ResetHeader(data_length, error, unique);
 }
 
 void FuseBuffer::HandleInit() {
@@ -222,5 +238,11 @@
   response.Reset(0, -ENOSYS, unique);
 }
 
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+template class FuseMessage<FuseSimpleResponse>;
+template struct FuseResponseBase<0u>;
+template struct FuseResponseBase<kFuseMaxRead>;
+
 }  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index fbb05d6..7a70bf3 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -43,11 +43,9 @@
  public:
   bool Read(int fd);
   bool Write(int fd) const;
+  bool WriteWithBody(int fd, size_t max_size, const void* data) const;
   ResultOrAgain ReadOrAgain(int fd);
   ResultOrAgain WriteOrAgain(int fd) const;
-
-private:
-  bool CheckHeaderLength(const char* name) const;
 };
 
 // FuseRequest represents file operation requests from /dev/fuse. It starts
@@ -74,26 +72,30 @@
 
 // FuseResponse represents file operation responses to /dev/fuse. It starts
 // from fuse_out_header. The body layout depends on the operation code.
-struct FuseResponse : public FuseMessage<FuseResponse> {
-  fuse_out_header header;
-  union {
-    // for FUSE_INIT
-    fuse_init_out init_out;
-    // for FUSE_LOOKUP
-    fuse_entry_out entry_out;
-    // for FUSE_GETATTR
-    fuse_attr_out attr_out;
-    // for FUSE_OPEN
-    fuse_open_out open_out;
-    // for FUSE_READ
-    char read_data[kFuseMaxRead];
-    // for FUSE_WRITE
-    fuse_write_out write_out;
-  };
-  void Reset(uint32_t data_length, int32_t error, uint64_t unique);
-  void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
+template <size_t N>
+struct FuseResponseBase : public FuseMessage<FuseResponseBase<N>> {
+    fuse_out_header header;
+    union {
+        // for FUSE_INIT
+        fuse_init_out init_out;
+        // for FUSE_LOOKUP
+        fuse_entry_out entry_out;
+        // for FUSE_GETATTR
+        fuse_attr_out attr_out;
+        // for FUSE_OPEN
+        fuse_open_out open_out;
+        // for FUSE_READ
+        char read_data[N];
+        // for FUSE_WRITE
+        fuse_write_out write_out;
+    };
+    void Reset(uint32_t data_length, int32_t error, uint64_t unique);
+    void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
 };
 
+using FuseResponse = FuseResponseBase<kFuseMaxRead>;
+using FuseSimpleResponse = FuseResponseBase<0u>;
+
 // To reduce memory usage, FuseBuffer shares the memory region for request and
 // response.
 union FuseBuffer final {