diff options
author | David Anderson <dvander@google.com> | 2019-07-11 13:09:42 -0700 |
---|---|---|
committer | David Anderson <dvander@google.com> | 2019-07-11 15:39:53 -0700 |
commit | 4f9d1b15b48175d380abc85ed9e1fbba4fcbd96e (patch) | |
tree | 1fafe234995edbd603039b4a3d63e3f9ce080a55 /fs_mgr/libdm/loop_control.cpp | |
parent | 470fe2b5f07aa947dc61f5978803d194b5df33d8 (diff) |
libdm: Fix race conditions in LoopControl::Attach.
This fixes two race conditions in LoopControl::Attach(). The first is
that after LOOP_CTL_GET_FREE, the path is not be available until it has
been processed by ueventd. This can be fixed by adding a timeout
parameter and a call to WaitForFile().
Second, it is possible (albeit unlikely), given that loop devices are
now being used more aggressively, that two processes race when
attempting LOOP_SET_FD. In this case, one process will win, and the
other will fail with EBUSY. We can handle this case by retrying the
operation while respecting the same timeout parameter.
Bug: 135771280
Test: libdm_test gtest
Change-Id: Icf9facc3ca28fdb6ff5c78612d3dc183fa47b1f3
Diffstat (limited to 'fs_mgr/libdm/loop_control.cpp')
-rw-r--r-- | fs_mgr/libdm/loop_control.cpp | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp index 16bf4b038..edc9a459a 100644 --- a/fs_mgr/libdm/loop_control.cpp +++ b/fs_mgr/libdm/loop_control.cpp @@ -27,6 +27,8 @@ #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> +#include "utility.h" + namespace android { namespace dm { @@ -37,21 +39,40 @@ LoopControl::LoopControl() : control_fd_(-1) { } } -bool LoopControl::Attach(int file_fd, std::string* loopdev) const { - if (!FindFreeLoopDevice(loopdev)) { - LOG(ERROR) << "Failed to attach, no free loop devices"; - return false; - } - - android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC))); - if (loop_fd < 0) { - PLOG(ERROR) << "Failed to open: " << *loopdev; - return false; - } - - int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); - if (rc < 0) { - PLOG(ERROR) << "Failed LOOP_SET_FD"; +bool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms, + std::string* loopdev) const { + auto start_time = std::chrono::steady_clock::now(); + auto condition = [&]() -> WaitResult { + if (!FindFreeLoopDevice(loopdev)) { + LOG(ERROR) << "Failed to attach, no free loop devices"; + return WaitResult::Fail; + } + + auto now = std::chrono::steady_clock::now(); + auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time); + if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) { + LOG(ERROR) << "Timed out waiting for path: " << *loopdev; + return WaitResult::Fail; + } + + android::base::unique_fd loop_fd( + TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC))); + if (loop_fd < 0) { + PLOG(ERROR) << "Failed to open: " << *loopdev; + return WaitResult::Fail; + } + + if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) { + return WaitResult::Done; + } + if (errno != EBUSY) { + PLOG(ERROR) << "Failed LOOP_SET_FD"; + return WaitResult::Fail; + } + return WaitResult::Wait; + }; + if (!WaitForCondition(condition, timeout_ms)) { + LOG(ERROR) << "Timed out trying to acquire a loop device"; return false; } return true; @@ -112,17 +133,19 @@ bool LoopControl::EnableDirectIo(int fd) { return true; } -LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) { - Init(); +LoopDevice::LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close) + : fd_(fd), owns_fd_(auto_close) { + Init(timeout_ms); } -LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) { +LoopDevice::LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms) + : fd_(-1), owns_fd_(true) { fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC)); if (fd_ < -1) { PLOG(ERROR) << "open failed for " << path; return; } - Init(); + Init(timeout_ms); } LoopDevice::~LoopDevice() { @@ -134,8 +157,8 @@ LoopDevice::~LoopDevice() { } } -void LoopDevice::Init() { - control_.Attach(fd_, &device_); +void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) { + valid_ = control_.Attach(fd_, timeout_ms, &device_); } } // namespace dm |