1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <fs_mgr/file_wait.h> 16 17 #include <limits.h> 18 #if defined(__linux__) 19 #include <poll.h> 20 #include <sys/inotify.h> 21 #endif 22 #if defined(WIN32) 23 #include <io.h> 24 #else 25 #include <unistd.h> 26 #endif 27 28 #include <functional> 29 #include <thread> 30 31 #include <android-base/file.h> 32 #include <android-base/logging.h> 33 #include <android-base/unique_fd.h> 34 35 namespace android { 36 namespace fs_mgr { 37 38 using namespace std::literals; 39 using android::base::unique_fd; 40 41 bool PollForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) { 42 auto start_time = std::chrono::steady_clock::now(); 43 44 while (true) { 45 if (!access(path.c_str(), F_OK) || errno != ENOENT) return true; 46 47 std::this_thread::sleep_for(50ms); 48 49 auto now = std::chrono::steady_clock::now(); 50 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time); 51 if (time_elapsed > relative_timeout) return false; 52 } 53 } 54 55 bool PollForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) { 56 auto start_time = std::chrono::steady_clock::now(); 57 58 while (true) { 59 if (access(path.c_str(), F_OK) && errno == ENOENT) return true; 60 61 std::this_thread::sleep_for(50ms); 62 63 auto now = std::chrono::steady_clock::now(); 64 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time); 65 if (time_elapsed > relative_timeout) return false; 66 } 67 } 68 69 #if defined(__linux__) 70 class OneShotInotify { 71 public: 72 OneShotInotify(const std::string& path, uint32_t mask, 73 const std::chrono::milliseconds relative_timeout); 74 75 bool Wait(); 76 77 private: 78 bool CheckCompleted(); 79 int64_t RemainingMs() const; 80 bool ConsumeEvents(); 81 82 enum class Result { Success, Timeout, Error }; 83 Result WaitImpl(); 84 85 unique_fd inotify_fd_; 86 std::string path_; 87 uint32_t mask_; 88 std::chrono::time_point<std::chrono::steady_clock> start_time_; 89 std::chrono::milliseconds relative_timeout_; 90 bool finished_; 91 }; 92 93 OneShotInotify::OneShotInotify(const std::string& path, uint32_t mask, 94 const std::chrono::milliseconds relative_timeout) 95 : path_(path), 96 mask_(mask), 97 start_time_(std::chrono::steady_clock::now()), 98 relative_timeout_(relative_timeout), 99 finished_(false) { 100 // If the condition is already met, don't bother creating an inotify. 101 if (CheckCompleted()) return; 102 103 unique_fd inotify_fd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK)); 104 if (inotify_fd < 0) { 105 PLOG(ERROR) << "inotify_init1 failed"; 106 return; 107 } 108 109 std::string watch_path; 110 if (mask == IN_CREATE) { 111 watch_path = android::base::Dirname(path); 112 } else { 113 watch_path = path; 114 } 115 if (inotify_add_watch(inotify_fd, watch_path.c_str(), mask) < 0) { 116 PLOG(ERROR) << "inotify_add_watch failed"; 117 return; 118 } 119 120 // It's possible the condition was met before the add_watch. Check for 121 // this and abort early if so. 122 if (CheckCompleted()) return; 123 124 inotify_fd_ = std::move(inotify_fd); 125 } 126 127 bool OneShotInotify::Wait() { 128 Result result = WaitImpl(); 129 if (result == Result::Success) return true; 130 if (result == Result::Timeout) return false; 131 132 // Some kind of error with inotify occurred, so fallback to a poll. 133 std::chrono::milliseconds timeout(RemainingMs()); 134 if (mask_ == IN_CREATE) { 135 return PollForFile(path_, timeout); 136 } else if (mask_ == IN_DELETE_SELF) { 137 return PollForFileDeleted(path_, timeout); 138 } else { 139 LOG(ERROR) << "Unknown inotify mask: " << mask_; 140 return false; 141 } 142 } 143 144 OneShotInotify::Result OneShotInotify::WaitImpl() { 145 // If the operation completed super early, we'll never have created an 146 // inotify instance. 147 if (finished_) return Result::Success; 148 if (inotify_fd_ < 0) return Result::Error; 149 150 while (true) { 151 auto remaining_ms = RemainingMs(); 152 if (remaining_ms <= 0) return Result::Timeout; 153 154 struct pollfd event = { 155 .fd = inotify_fd_, 156 .events = POLLIN, 157 .revents = 0, 158 }; 159 int rv = poll(&event, 1, static_cast<int>(remaining_ms)); 160 if (rv <= 0) { 161 if (rv == 0 || errno == EINTR) { 162 continue; 163 } 164 PLOG(ERROR) << "poll for inotify failed"; 165 return Result::Error; 166 } 167 if (event.revents & POLLERR) { 168 LOG(ERROR) << "error reading inotify for " << path_; 169 return Result::Error; 170 } 171 172 // Note that we don't bother checking what kind of event it is, since 173 // it's cheap enough to just see if the initial condition is satisified. 174 // If it's not, we consume all the events available and continue. 175 if (CheckCompleted()) return Result::Success; 176 if (!ConsumeEvents()) return Result::Error; 177 } 178 } 179 180 bool OneShotInotify::CheckCompleted() { 181 if (mask_ == IN_CREATE) { 182 finished_ = !access(path_.c_str(), F_OK) || errno != ENOENT; 183 } else if (mask_ == IN_DELETE_SELF) { 184 finished_ = access(path_.c_str(), F_OK) && errno == ENOENT; 185 } else { 186 LOG(ERROR) << "Unexpected mask: " << mask_; 187 } 188 return finished_; 189 } 190 191 bool OneShotInotify::ConsumeEvents() { 192 // According to the manpage, this is enough to read at least one event. 193 static constexpr size_t kBufferSize = sizeof(struct inotify_event) + NAME_MAX + 1; 194 char buffer[kBufferSize]; 195 196 do { 197 ssize_t rv = TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer))); 198 if (rv <= 0) { 199 if (rv == 0 || errno == EAGAIN) { 200 return true; 201 } 202 PLOG(ERROR) << "read inotify failed"; 203 return false; 204 } 205 } while (true); 206 } 207 208 int64_t OneShotInotify::RemainingMs() const { 209 auto remaining = (std::chrono::steady_clock::now() - start_time_); 210 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining); 211 return (relative_timeout_ - elapsed).count(); 212 } 213 #endif 214 215 bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) { 216 #if defined(__linux__) 217 OneShotInotify inotify(path, IN_CREATE, relative_timeout); 218 return inotify.Wait(); 219 #else 220 return PollForFile(path, relative_timeout); 221 #endif 222 } 223 224 // Wait at most |relative_timeout| milliseconds for |path| to stop existing. 225 bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) { 226 #if defined(__linux__) 227 OneShotInotify inotify(path, IN_DELETE_SELF, relative_timeout); 228 return inotify.Wait(); 229 #else 230 return PollForFileDeleted(path, relative_timeout); 231 #endif 232 } 233 234 } // namespace fs_mgr 235 } // namespace android 236