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 DCHECK_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 read_only_mode_ = other.read_only_mode_; 95 other.Release(); // Release other. 96 97 return *this; 98 } 99 100 FdFile::~FdFile() { 101 Destroy(); 102 } 103 104 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) { 105 if (kCheckSafeUsage) { 106 if (guard_state_ < GuardState::kNoCheck) { 107 if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) { 108 LOG(ERROR) << warning; 109 } 110 guard_state_ = target; 111 } 112 } 113 } 114 115 void FdFile::moveUp(GuardState target, const char* warning) { 116 if (kCheckSafeUsage) { 117 if (guard_state_ < GuardState::kNoCheck) { 118 if (guard_state_ < target) { 119 guard_state_ = target; 120 } else if (target < guard_state_) { 121 LOG(ERROR) << warning; 122 } 123 } 124 } 125 } 126 127 void FdFile::DisableAutoClose() { 128 auto_close_ = false; 129 } 130 131 bool FdFile::Open(const std::string& path, int flags) { 132 return Open(path, flags, 0640); 133 } 134 135 bool FdFile::Open(const std::string& path, int flags, mode_t mode) { 136 static_assert(O_RDONLY == 0, "Readonly flag has unexpected value."); 137 DCHECK_EQ(fd_, -1) << path; 138 read_only_mode_ = ((flags & O_ACCMODE) == O_RDONLY); 139 fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); 140 if (fd_ == -1) { 141 return false; 142 } 143 file_path_ = path; 144 if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) { 145 // Start in the base state (not flushed, not closed). 146 guard_state_ = GuardState::kBase; 147 } else { 148 // We are not concerned with read-only files. In that case, proper flushing and closing is 149 // not important. 150 guard_state_ = GuardState::kNoCheck; 151 } 152 return true; 153 } 154 155 int FdFile::Close() { 156 int result = close(fd_); 157 158 // Test here, so the file is closed and not leaked. 159 if (kCheckSafeUsage) { 160 DCHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_ 161 << " has not been flushed before closing."; 162 moveUp(GuardState::kClosed, nullptr); 163 } 164 165 if (result == -1) { 166 return -errno; 167 } else { 168 fd_ = -1; 169 file_path_ = ""; 170 return 0; 171 } 172 } 173 174 int FdFile::Flush() { 175 DCHECK(!read_only_mode_); 176 177 #ifdef __linux__ 178 int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); 179 #else 180 int rc = TEMP_FAILURE_RETRY(fsync(fd_)); 181 #endif 182 183 moveUp(GuardState::kFlushed, "Flushing closed file."); 184 if (rc == 0) { 185 return 0; 186 } 187 188 // Don't report failure if we just tried to flush a pipe or socket. 189 return errno == EINVAL ? 0 : -errno; 190 } 191 192 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { 193 #ifdef __linux__ 194 int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset)); 195 #else 196 int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset)); 197 #endif 198 return (rc == -1) ? -errno : rc; 199 } 200 201 int FdFile::SetLength(int64_t new_length) { 202 DCHECK(!read_only_mode_); 203 #ifdef __linux__ 204 int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); 205 #else 206 int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length)); 207 #endif 208 moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file."); 209 return (rc == -1) ? -errno : rc; 210 } 211 212 int64_t FdFile::GetLength() const { 213 struct stat s; 214 int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s)); 215 return (rc == -1) ? -errno : s.st_size; 216 } 217 218 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { 219 DCHECK(!read_only_mode_); 220 #ifdef __linux__ 221 int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); 222 #else 223 int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset)); 224 #endif 225 moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); 226 return (rc == -1) ? -errno : rc; 227 } 228 229 int FdFile::Fd() const { 230 return fd_; 231 } 232 233 bool FdFile::ReadOnlyMode() const { 234 return read_only_mode_; 235 } 236 237 bool FdFile::CheckUsage() const { 238 return guard_state_ != GuardState::kNoCheck; 239 } 240 241 bool FdFile::IsOpened() const { 242 return fd_ >= 0; 243 } 244 245 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) { 246 DCHECK_EQ(offset, 0); 247 return read(fd, buf, count); 248 } 249 250 template <ssize_t (*read_func)(int, void*, size_t, off_t)> 251 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) { 252 char* ptr = static_cast<char*>(buffer); 253 while (byte_count > 0) { 254 ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset)); 255 if (bytes_read <= 0) { 256 // 0: end of file 257 // -1: error 258 return false; 259 } 260 byte_count -= bytes_read; // Reduce the number of remaining bytes. 261 ptr += bytes_read; // Move the buffer forward. 262 offset += static_cast<size_t>(bytes_read); // Move the offset forward. 263 } 264 return true; 265 } 266 267 bool FdFile::ReadFully(void* buffer, size_t byte_count) { 268 return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0); 269 } 270 271 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { 272 return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset); 273 } 274 275 template <bool kUseOffset> 276 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) { 277 DCHECK(!read_only_mode_); 278 moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); 279 DCHECK(kUseOffset || offset == 0u); 280 const char* ptr = static_cast<const char*>(buffer); 281 while (byte_count > 0) { 282 ssize_t bytes_written = kUseOffset 283 ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset)) 284 : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); 285 if (bytes_written == -1) { 286 return false; 287 } 288 byte_count -= bytes_written; // Reduce the number of remaining bytes. 289 ptr += bytes_written; // Move the buffer forward. 290 offset += static_cast<size_t>(bytes_written); 291 } 292 return true; 293 } 294 295 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) { 296 return WriteFullyGeneric<true>(buffer, byte_count, offset); 297 } 298 299 bool FdFile::WriteFully(const void* buffer, size_t byte_count) { 300 return WriteFullyGeneric<false>(buffer, byte_count, 0u); 301 } 302 303 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { 304 DCHECK(!read_only_mode_); 305 off_t off = static_cast<off_t>(offset); 306 off_t sz = static_cast<off_t>(size); 307 if (offset < 0 || static_cast<int64_t>(off) != offset || 308 size < 0 || static_cast<int64_t>(sz) != size || 309 sz > std::numeric_limits<off_t>::max() - off) { 310 errno = EINVAL; 311 return false; 312 } 313 if (size == 0) { 314 return true; 315 } 316 #ifdef __linux__ 317 // Use sendfile(), available for files since linux kernel 2.6.33. 318 off_t end = off + sz; 319 while (off != end) { 320 int result = TEMP_FAILURE_RETRY( 321 sendfile(Fd(), input_file->Fd(), &off, end - off)); 322 if (result == -1) { 323 return false; 324 } 325 // Ignore the number of bytes in `result`, sendfile() already updated `off`. 326 } 327 #else 328 if (lseek(input_file->Fd(), off, SEEK_SET) != off) { 329 return false; 330 } 331 constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize; 332 const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize); 333 art::UniqueCPtr<void> buffer(malloc(buffer_size)); 334 if (buffer == nullptr) { 335 errno = ENOMEM; 336 return false; 337 } 338 while (size != 0) { 339 size_t chunk_size = std::min<uint64_t>(buffer_size, size); 340 if (!input_file->ReadFully(buffer.get(), chunk_size) || 341 !WriteFully(buffer.get(), chunk_size)) { 342 return false; 343 } 344 size -= chunk_size; 345 } 346 #endif 347 return true; 348 } 349 350 bool FdFile::Unlink() { 351 if (file_path_.empty()) { 352 return false; 353 } 354 355 // Try to figure out whether this file is still referring to the one on disk. 356 bool is_current = false; 357 { 358 struct stat this_stat, current_stat; 359 int cur_fd = TEMP_FAILURE_RETRY(open(file_path_.c_str(), O_RDONLY)); 360 if (cur_fd > 0) { 361 // File still exists. 362 if (fstat(fd_, &this_stat) == 0 && fstat(cur_fd, ¤t_stat) == 0) { 363 is_current = (this_stat.st_dev == current_stat.st_dev) && 364 (this_stat.st_ino == current_stat.st_ino); 365 } 366 close(cur_fd); 367 } 368 } 369 370 if (is_current) { 371 unlink(file_path_.c_str()); 372 } 373 374 return is_current; 375 } 376 377 bool FdFile::Erase(bool unlink) { 378 DCHECK(!read_only_mode_); 379 380 bool ret_result = true; 381 if (unlink) { 382 ret_result = Unlink(); 383 } 384 385 int result; 386 result = SetLength(0); 387 result = Flush(); 388 result = Close(); 389 // Ignore the errors. 390 391 return ret_result; 392 } 393 394 int FdFile::FlushCloseOrErase() { 395 DCHECK(!read_only_mode_); 396 int flush_result = Flush(); 397 if (flush_result != 0) { 398 LOG(ERROR) << "CloseOrErase failed while flushing a file."; 399 Erase(); 400 return flush_result; 401 } 402 int close_result = Close(); 403 if (close_result != 0) { 404 LOG(ERROR) << "CloseOrErase failed while closing a file."; 405 Erase(); 406 return close_result; 407 } 408 return 0; 409 } 410 411 int FdFile::FlushClose() { 412 DCHECK(!read_only_mode_); 413 int flush_result = Flush(); 414 if (flush_result != 0) { 415 LOG(ERROR) << "FlushClose failed while flushing a file."; 416 } 417 int close_result = Close(); 418 if (close_result != 0) { 419 LOG(ERROR) << "FlushClose failed while closing a file."; 420 } 421 return (flush_result != 0) ? flush_result : close_result; 422 } 423 424 void FdFile::MarkUnchecked() { 425 guard_state_ = GuardState::kNoCheck; 426 } 427 428 bool FdFile::ClearContent() { 429 DCHECK(!read_only_mode_); 430 if (SetLength(0) < 0) { 431 PLOG(ERROR) << "Failed to reset the length"; 432 return false; 433 } 434 return ResetOffset(); 435 } 436 437 bool FdFile::ResetOffset() { 438 DCHECK(!read_only_mode_); 439 off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); 440 if (rc == static_cast<off_t>(-1)) { 441 PLOG(ERROR) << "Failed to reset the offset"; 442 return false; 443 } 444 return true; 445 } 446 447 int FdFile::Compare(FdFile* other) { 448 int64_t length = GetLength(); 449 int64_t length2 = other->GetLength(); 450 if (length != length2) { 451 return length < length2 ? -1 : 1; 452 } 453 static const size_t kBufferSize = 4096; 454 std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]); 455 std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]); 456 while (length > 0) { 457 size_t len = std::min(kBufferSize, static_cast<size_t>(length)); 458 if (!ReadFully(&buffer1[0], len)) { 459 return -1; 460 } 461 if (!other->ReadFully(&buffer2[0], len)) { 462 return 1; 463 } 464 int result = memcmp(&buffer1[0], &buffer2[0], len); 465 if (result != 0) { 466 return result; 467 } 468 length -= len; 469 } 470 return 0; 471 } 472 473 } // namespace unix_file 474