libfiemap: Handle EAGAIN in fallocate().

When we changed our ENOSPC tests, it exposed a path in F2FS were
fallocate can return EAGAIN. This is expected if F2FS attempts GC to
acquire more chunks, and it can leave the file in a partially allocated
state.

As a fix, keep attempting fallocate() as long as (1) it returns EAGAIN,
and (2) the allocated size keeps growing. If (2) fails we return ENOSPC.

Bug: N/A
Test: treehugger
Change-Id: I5f867b5a200b9260e486985f203f9872a949b3f9
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 275388e..06e210e 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -458,9 +458,34 @@
             return FiemapStatus::Error();
     }
 
-    if (fallocate(file_fd, 0, 0, file_size)) {
-        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return FiemapStatus::FromErrno(errno);
+    // F2FS can return EAGAIN and partially fallocate. Keep trying to fallocate,
+    // and if we don't make forward progress, return ENOSPC.
+    std::optional<off_t> prev_size;
+    while (true) {
+        if (fallocate(file_fd, 0, 0, file_size) == 0) {
+            break;
+        }
+        if (errno != EAGAIN) {
+            PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+                        << " size: " << file_size;
+            return FiemapStatus::FromErrno(errno);
+        }
+
+        struct stat s;
+        if (fstat(file_fd, &s) < 0) {
+            PLOG(ERROR) << "Failed to fstat after fallocate failure: " << file_path;
+            return FiemapStatus::FromErrno(errno);
+        }
+        if (!prev_size) {
+            prev_size = {s.st_size};
+            continue;
+        }
+        if (*prev_size >= s.st_size) {
+            LOG(ERROR) << "Fallocate retry failed, got " << s.st_size << ", asked for "
+                       << file_size;
+            return FiemapStatus(FiemapStatus::ErrorCode::NO_SPACE);
+        }
+        LOG(INFO) << "Retrying fallocate, got " << s.st_size << ", asked for " << file_size;
     }
 
     if (need_explicit_writes) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
index d7b2cf1..1365ba4 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -56,8 +56,7 @@
     // For logging and debugging only.
     std::string string() const;
 
-  protected:
-    FiemapStatus(ErrorCode code) : error_code_(code) {}
+    explicit FiemapStatus(ErrorCode code) : error_code_(code) {}
 
   private:
     ErrorCode error_code_;