Merge changes from topic "fastbootd-reset-fd"

* changes:
  fastbootd: reset file descriptor on unaligned writes
  fastbootd: add support to reset fd by handle
  fastbootd: pass handle in place of fd
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 22f8363..06ffe0f 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -76,7 +76,7 @@
 
 }  // namespace
 
-int FlashRawDataChunk(int fd, const char* data, size_t len) {
+int FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {
     size_t ret = 0;
     const size_t max_write_size = 1048576;
     void* aligned_buffer;
@@ -91,7 +91,15 @@
     while (ret < len) {
         int this_len = std::min(max_write_size, len - ret);
         memcpy(aligned_buffer_unique_ptr.get(), data, this_len);
-        int this_ret = write(fd, aligned_buffer_unique_ptr.get(), this_len);
+        // In case of non 4KB aligned writes, reopen without O_DIRECT flag
+        if (this_len & 0xFFF) {
+            if (handle->Reset(O_WRONLY) != true) {
+                PLOG(ERROR) << "Failed to reset file descriptor";
+                return -1;
+            }
+        }
+
+        int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);
         if (this_ret < 0) {
             PLOG(ERROR) << "Failed to flash data of len " << len;
             return -1;
@@ -102,8 +110,8 @@
     return 0;
 }
 
-int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
-    int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+int FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {
+    int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());
     if (ret < 0) {
         return -errno;
     }
@@ -111,30 +119,30 @@
 }
 
 int WriteCallback(void* priv, const void* data, size_t len) {
-    int fd = reinterpret_cast<long long>(priv);
+    PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
     if (!data) {
-        return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+        return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
     }
-    return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+    return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
 }
 
-int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+int FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {
     struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),
                                                       downloaded_data.size(), true, false);
     if (!file) {
         // Invalid sparse format
         return -EINVAL;
     }
-    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
 }
 
-int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
-    lseek64(fd, 0, SEEK_SET);
+int FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {
+    lseek64(handle->fd(), 0, SEEK_SET);
     if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
         *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
-        return FlashSparseData(fd, downloaded_data);
+        return FlashSparseData(handle, downloaded_data);
     } else {
-        return FlashRawData(fd, downloaded_data);
+        return FlashRawData(handle, downloaded_data);
     }
 }
 
@@ -181,7 +189,7 @@
     if (android::base::GetProperty("ro.system.build.type", "") != "user") {
         WipeOverlayfsForPartition(device, partition_name);
     }
-    int result = FlashBlockDevice(handle.fd(), data);
+    int result = FlashBlockDevice(&handle, data);
     sync();
     return result;
 }
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 97b5ad4..3302c43 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -90,14 +90,7 @@
         return false;
     }
 
-    flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
-    unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to open block device: " << handle->path();
-        return false;
-    }
-    handle->set_fd(std::move(fd));
-    return true;
+    return handle->Open(flags);
 }
 
 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 1d81b7a..6e1453f 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -18,6 +18,8 @@
 #include <optional>
 #include <string>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <fstab/fstab.h>
@@ -44,11 +46,51 @@
     }
     const std::string& path() const { return path_; }
     int fd() const { return fd_.get(); }
-    void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+    bool Open(int flags) {
+        flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
 
+        // Attempts to open a second device can fail with EBUSY if the device is already open.
+        // Explicitly close any previously opened devices as unique_fd won't close them until
+        // after the attempt to open.
+        fd_.reset();
+
+        fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open block device: " << path_;
+            return false;
+        }
+        flags_ = flags;
+
+        return true;
+    }
+    bool Reset(int flags) {
+        if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {
+            return true;
+        }
+
+        off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;
+        if (offset < 0) {
+            PLOG(ERROR) << "Failed lseek on block device: " << path_;
+            return false;
+        }
+
+        sync();
+
+        if (Open(flags) == false) {
+            return false;
+        }
+
+        if (lseek(fd_.get(), offset, SEEK_SET) != offset) {
+            PLOG(ERROR) << "Failed lseek on block device: " << path_;
+            return false;
+        }
+
+        return true;
+    }
   private:
     std::string path_;
     android::base::unique_fd fd_;
+    int flags_;
     std::function<void()> closer_;
 };