1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "base/unix_file/fd_file.h" 18 19 #include <errno.h> 20 #include <limits> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 #include "base/logging.h" 26 27 // Includes needed for FdFile::Copy(). 28 #ifdef __linux__ 29 #include <sys/sendfile.h> 30 #else 31 #include <algorithm> 32 #include "base/stl_util.h" 33 #include "globals.h" 34 #endif 35 36 namespace unix_file { 37 38 FdFile::FdFile() 39 : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) { 40 } 41 42 FdFile::FdFile(int fd, bool check_usage) 43 : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), 44 fd_(fd), auto_close_(true), read_only_mode_(false) { 45 } 46 47 FdFile::FdFile(int fd, const std::string& path, bool check_usage) 48 : FdFile(fd, path, check_usage, false) { 49 } 50 51 FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode) 52 : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), 53 fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) { 54 } 55 56 FdFile::FdFile(const std::string& path, int flags, mode_t mode, bool check_usage) 57 : fd_(-1), auto_close_(true) { 58 Open(path, flags, mode); 59 if (!check_usage || !IsOpened()) { 60 guard_state_ = GuardState::kNoCheck; 61 } 62 } 63 64 void FdFile::Destroy() { 65 if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) { 66 if (guard_state_ < GuardState::kFlushed) { 67 LOG(ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction."; 68 } 69 if (guard_state_ < GuardState::kClosed) { 70 LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction."; 71 } 72 CHECK_GE(guard_state_, GuardState::kClosed); 73 } 74 if (auto_close_ && fd_ != -1) { 75 if (Close() != 0) { 76 PLOG(WARNING) << "Failed to close file with fd=" << fd_ << " path=" << file_path_; 77 } 78 } 79 } 80 81 FdFile& FdFile::operator=(FdFile&& other) { 82 if (this == &other) { 83 return *this; 84 } 85 86 if (this->fd_ != other.fd_) { 87 Destroy(); // Free old state. 88 } 89 90 guard_state_ = other.guard_state_; 91 fd_ = other.fd_; 92 file_path_ = std::move(other.file_path_); 93 auto_close_ = other.auto_close_; 94 other.Release(); // Release other. 95 96 return *this; 97 } 98 99 FdFile::~FdFile() { 100 Destroy(); 101 } 102 103 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) { 104 if (kCheckSafeUsage) { 105 if (guard_state_ < GuardState::kNoCheck) { 106 if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) { 107 LOG(ERROR) << warning; 108 } 109 guard_state_ = target; 110 } 111 } 112 } 113 114 void FdFile::moveUp(GuardState target, const char* warning) { 115 if (kCheckSafeUsage) { 116 if (guard_state_ < GuardState::kNoCheck) { 117 if (guard_state_ < target) { 118 guard_state_ = target; 119 } else if (target < guard_state_) { 120 LOG(ERROR) << warning; 121 } 122 } 123 } 124 } 125 126 void FdFile::DisableAutoClose() { 127 auto_close_ = false; 128 } 129 130 bool FdFile::Open(const std::string& path, int flags) { 131 return Open(path, flags, 0640); 132 } 133 134 bool FdFile::Open(const std::string& path, int flags, mode_t mode) { 135 static_assert(O_RDONLY == 0, "Readonly flag has unexpected value."); 136 CHECK_EQ(fd_, -1) << path; 137 read_only_mode_ = ((flags & O_ACCMODE) == O_RDONLY); 138 fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); 139 if (fd_ == -1) { 140 return false; 141 } 142 file_path_ = path; 143 if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) { 144 // Start in the base state (not flushed, not closed). 145 guard_state_ = GuardState::kBase; 146 } else { 147 // We are not concerned with read-only files. In that case, proper flushing and closing is 148 // not important. 149 guard_state_ = GuardState::kNoCheck; 150 } 151 return true; 152 } 153 154 int FdFile::Close() { 155 int result = close(fd_); 156 157 // Test here, so the file is closed and not leaked. 158 if (kCheckSafeUsage) { 159 CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_ 160 << " has not been flushed before closing."; 161 moveUp(GuardState::kClosed, nullptr); 162 } 163 164 if (result == -1) { 165 return -errno; 166 } else { 167 fd_ = -1; 168 file_path_ = ""; 169 return 0; 170 } 171 } 172 173 int FdFile::Flush() { 174 DCHECK(!read_only_mode_); 175 #ifdef __linux__ 176 int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); 177 #else 178 int rc = TEMP_FAILURE_RETRY(fsync(fd_)); 179 #endif 180 moveUp(GuardState::kFlushed, "Flushing closed file."); 181 return (rc == -1) ? -errno : rc; 182 } 183 184 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { 185 #ifdef __linux__ 186 int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset)); 187 #else 188 int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset)); 189 #endif 190 return (rc == -1) ? -errno : rc; 191 } 192 193 int FdFile::SetLength(int64_t new_length) { 194 DCHECK(!read_only_mode_); 195 #ifdef __linux__ 196 int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); 197 #else 198 int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length)); 199 #endif 200 moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file."); 201 return (rc == -1) ? -errno : rc; 202 } 203 204 int64_t FdFile::GetLength() const { 205 struct stat s; 206 int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s)); 207 return (rc == -1) ? -errno : s.st_size; 208 } 209 210 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { 211 DCHECK(!read_only_mode_); 212 #ifdef __linux__ 213 int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); 214 #else 215 int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset)); 216 #endif 217 moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); 218 return (rc == -1) ? -errno : rc; 219 } 220 221 int FdFile::Fd() const { 222 return fd_; 223 } 224 225 bool FdFile::ReadOnlyMode() const { 226 return read_only_mode_; 227 } 228 229 bool FdFile::CheckUsage() const { 230 return guard_state_ != GuardState::kNoCheck; 231 } 232 233 bool FdFile::IsOpened() const { 234 return fd_ >= 0; 235 } 236 237 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) { 238 DCHECK_EQ(offset, 0); 239 return read(fd, buf, count); 240 } 241 242 template <ssize_t (*read_func)(int, void*, size_t, off_t)> 243 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) { 244 char* ptr = static_cast<char*>(buffer); 245 while (byte_count > 0) { 246 ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset)); 247 if (bytes_read <= 0) { 248 // 0: end of file 249 // -1: error 250 return false; 251 } 252 byte_count -= bytes_read; // Reduce the number of remaining bytes. 253 ptr += bytes_read; // Move the buffer forward. 254 offset += static_cast<size_t>(bytes_read); // Move the offset forward. 255 } 256 return true; 257 } 258 259 bool FdFile::ReadFully(void* buffer, size_t byte_count) { 260 return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0); 261 } 262 263 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { 264 return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset); 265 } 266 267 template <bool kUseOffset> 268 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) { 269 DCHECK(!read_only_mode_); 270 moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); 271 DCHECK(kUseOffset || offset == 0u); 272 const char* ptr = static_cast<const char*>(buffer); 273 while (byte_count > 0) { 274 ssize_t bytes_written = kUseOffset 275 ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset)) 276 : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); 277 if (bytes_written == -1) { 278 return false; 279 } 280 byte_count -= bytes_written; // Reduce the number of remaining bytes. 281 ptr += bytes_written; // Move the buffer forward. 282 offset += static_cast<size_t>(bytes_written); 283 } 284 return true; 285 } 286 287 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) { 288 return WriteFullyGeneric<true>(buffer, byte_count, offset); 289 } 290 291 bool FdFile::WriteFully(const void* buffer, size_t byte_count) { 292 return WriteFullyGeneric<false>(buffer, byte_count, 0u); 293 } 294 295 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { 296 DCHECK(!read_only_mode_); 297 off_t off = static_cast<off_t>(offset); 298 off_t sz = static_cast<off_t>(size); 299 if (offset < 0 || static_cast<int64_t>(off) != offset || 300 size < 0 || static_cast<int64_t>(sz) != size || 301 sz > std::numeric_limits<off_t>::max() - off) { 302 errno = EINVAL; 303 return false; 304 } 305 if (size == 0) { 306 return true; 307 } 308 #ifdef __linux__ 309 // Use sendfile(), available for files since linux kernel 2.6.33. 310 off_t end = off + sz; 311 while (off != end) { 312 int result = TEMP_FAILURE_RETRY( 313 sendfile(Fd(), input_file->Fd(), &off, end - off)); 314 if (result == -1) { 315 return false; 316 } 317 // Ignore the number of bytes in `result`, sendfile() already updated `off`. 318 } 319 #else 320 if (lseek(input_file->Fd(), off, SEEK_SET) != off) { 321 return false; 322 } 323 constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize; 324 const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize); 325 art::UniqueCPtr<void> buffer(malloc(buffer_size)); 326 if (buffer == nullptr) { 327 errno = ENOMEM; 328 return false; 329 } 330 while (size != 0) { 331 size_t chunk_size = std::min<uint64_t>(buffer_size, size); 332 if (!input_file->ReadFully(buffer.get(), chunk_size) || 333 !WriteFully(buffer.get(), chunk_size)) { 334 return false; 335 } 336 size -= chunk_size; 337 } 338 #endif 339 return true; 340 } 341 342 bool FdFile::Unlink() { 343 if (file_path_.empty()) { 344 return false; 345 } 346 347 // Try to figure out whether this file is still referring to the one on disk. 348 bool is_current = false; 349 { 350 struct stat this_stat, current_stat; 351 int cur_fd = TEMP_FAILURE_RETRY(open(file_path_.c_str(), O_RDONLY)); 352 if (cur_fd > 0) { 353 // File still exists. 354 if (fstat(fd_, &this_stat) == 0 && fstat(cur_fd, ¤t_stat) == 0) { 355 is_current = (this_stat.st_dev == current_stat.st_dev) && 356 (this_stat.st_ino == current_stat.st_ino); 357 } 358 close(cur_fd); 359 } 360 } 361 362 if (is_current) { 363 unlink(file_path_.c_str()); 364 } 365 366 return is_current; 367 } 368 369 bool FdFile::Erase(bool unlink) { 370 DCHECK(!read_only_mode_); 371 372 bool ret_result = true; 373 if (unlink) { 374 ret_result = Unlink(); 375 } 376 377 int result; 378 result = SetLength(0); 379 result = Flush(); 380 result = Close(); 381 // Ignore the errors. 382 383 return ret_result; 384 } 385 386 int FdFile::FlushCloseOrErase() { 387 DCHECK(!read_only_mode_); 388 int flush_result = Flush(); 389 if (flush_result != 0) { 390 LOG(ERROR) << "CloseOrErase failed while flushing a file."; 391 Erase(); 392 return flush_result; 393 } 394 int close_result = Close(); 395 if (close_result != 0) { 396 LOG(ERROR) << "CloseOrErase failed while closing a file."; 397 Erase(); 398 return close_result; 399 } 400 return 0; 401 } 402 403 int FdFile::FlushClose() { 404 DCHECK(!read_only_mode_); 405 int flush_result = Flush(); 406 if (flush_result != 0) { 407 LOG(ERROR) << "FlushClose failed while flushing a file."; 408 } 409 int close_result = Close(); 410 if (close_result != 0) { 411 LOG(ERROR) << "FlushClose failed while closing a file."; 412 } 413 return (flush_result != 0) ? flush_result : close_result; 414 } 415 416 void FdFile::MarkUnchecked() { 417 guard_state_ = GuardState::kNoCheck; 418 } 419 420 bool FdFile::ClearContent() { 421 DCHECK(!read_only_mode_); 422 if (SetLength(0) < 0) { 423 PLOG(ERROR) << "Failed to reset the length"; 424 return false; 425 } 426 return ResetOffset(); 427 } 428 429 bool FdFile::ResetOffset() { 430 DCHECK(!read_only_mode_); 431 off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); 432 if (rc == static_cast<off_t>(-1)) { 433 PLOG(ERROR) << "Failed to reset the offset"; 434 return false; 435 } 436 return true; 437 } 438 439 } // namespace unix_file 440