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
This commit is contained in:
David Anderson 2023-05-15 16:43:53 -07:00
parent 59abbfe647
commit 0fa371076a
2 changed files with 29 additions and 5 deletions

View file

@ -458,9 +458,34 @@ static FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint
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) {

View file

@ -56,8 +56,7 @@ class FiemapStatus {
// 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_;