Home | History | Annotate | Download | only in unix_file
      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() {
     57   if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
     58     if (guard_state_ < GuardState::kFlushed) {
     59       LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
     60     }
     61     if (guard_state_ < GuardState::kClosed) {
     62       LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
     63     }
     64     CHECK_GE(guard_state_, GuardState::kClosed);
     65   }
     66   if (auto_close_ && fd_ != -1) {
     67     if (Close() != 0) {
     68       PLOG(::art::WARNING) << "Failed to close file " << file_path_;
     69     }
     70   }
     71 }
     72 
     73 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
     74   if (kCheckSafeUsage) {
     75     if (guard_state_ < GuardState::kNoCheck) {
     76       if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
     77         LOG(::art::ERROR) << warning;
     78       }
     79       guard_state_ = target;
     80     }
     81   }
     82 }
     83 
     84 void FdFile::moveUp(GuardState target, const char* warning) {
     85   if (kCheckSafeUsage) {
     86     if (guard_state_ < GuardState::kNoCheck) {
     87       if (guard_state_ < target) {
     88         guard_state_ = target;
     89       } else if (target < guard_state_) {
     90         LOG(::art::ERROR) << warning;
     91       }
     92     }
     93   }
     94 }
     95 
     96 void FdFile::DisableAutoClose() {
     97   auto_close_ = false;
     98 }
     99 
    100 bool FdFile::Open(const std::string& path, int flags) {
    101   return Open(path, flags, 0640);
    102 }
    103 
    104 bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
    105   CHECK_EQ(fd_, -1) << path;
    106   read_only_mode_ = (flags & O_RDONLY) != 0;
    107   fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
    108   if (fd_ == -1) {
    109     return false;
    110   }
    111   file_path_ = path;
    112   static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
    113   if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
    114     // Start in the base state (not flushed, not closed).
    115     guard_state_ = GuardState::kBase;
    116   } else {
    117     // We are not concerned with read-only files. In that case, proper flushing and closing is
    118     // not important.
    119     guard_state_ = GuardState::kNoCheck;
    120   }
    121   return true;
    122 }
    123 
    124 int FdFile::Close() {
    125   int result = close(fd_);
    126 
    127   // Test here, so the file is closed and not leaked.
    128   if (kCheckSafeUsage) {
    129     CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
    130         << " has not been flushed before closing.";
    131     moveUp(GuardState::kClosed, nullptr);
    132   }
    133 
    134   if (result == -1) {
    135     return -errno;
    136   } else {
    137     fd_ = -1;
    138     file_path_ = "";
    139     return 0;
    140   }
    141 }
    142 
    143 int FdFile::Flush() {
    144   DCHECK(!read_only_mode_);
    145 #ifdef __linux__
    146   int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
    147 #else
    148   int rc = TEMP_FAILURE_RETRY(fsync(fd_));
    149 #endif
    150   moveUp(GuardState::kFlushed, "Flushing closed file.");
    151   return (rc == -1) ? -errno : rc;
    152 }
    153 
    154 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
    155 #ifdef __linux__
    156   int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
    157 #else
    158   int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset));
    159 #endif
    160   return (rc == -1) ? -errno : rc;
    161 }
    162 
    163 int FdFile::SetLength(int64_t new_length) {
    164   DCHECK(!read_only_mode_);
    165 #ifdef __linux__
    166   int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
    167 #else
    168   int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
    169 #endif
    170   moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
    171   return (rc == -1) ? -errno : rc;
    172 }
    173 
    174 int64_t FdFile::GetLength() const {
    175   struct stat s;
    176   int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s));
    177   return (rc == -1) ? -errno : s.st_size;
    178 }
    179 
    180 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
    181   DCHECK(!read_only_mode_);
    182 #ifdef __linux__
    183   int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
    184 #else
    185   int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
    186 #endif
    187   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
    188   return (rc == -1) ? -errno : rc;
    189 }
    190 
    191 int FdFile::Fd() const {
    192   return fd_;
    193 }
    194 
    195 bool FdFile::ReadOnlyMode() const {
    196   return read_only_mode_;
    197 }
    198 
    199 bool FdFile::CheckUsage() const {
    200   return guard_state_ != GuardState::kNoCheck;
    201 }
    202 
    203 bool FdFile::IsOpened() const {
    204   return fd_ >= 0;
    205 }
    206 
    207 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
    208   DCHECK_EQ(offset, 0);
    209   return read(fd, buf, count);
    210 }
    211 
    212 template <ssize_t (*read_func)(int, void*, size_t, off_t)>
    213 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
    214   char* ptr = static_cast<char*>(buffer);
    215   while (byte_count > 0) {
    216     ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
    217     if (bytes_read <= 0) {
    218       // 0: end of file
    219       // -1: error
    220       return false;
    221     }
    222     byte_count -= bytes_read;  // Reduce the number of remaining bytes.
    223     ptr += bytes_read;  // Move the buffer forward.
    224     offset += static_cast<size_t>(bytes_read);  // Move the offset forward.
    225   }
    226   return true;
    227 }
    228 
    229 bool FdFile::ReadFully(void* buffer, size_t byte_count) {
    230   return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
    231 }
    232 
    233 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
    234   return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
    235 }
    236 
    237 template <bool kUseOffset>
    238 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
    239   DCHECK(!read_only_mode_);
    240   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
    241   DCHECK(kUseOffset || offset == 0u);
    242   const char* ptr = static_cast<const char*>(buffer);
    243   while (byte_count > 0) {
    244     ssize_t bytes_written = kUseOffset
    245         ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
    246         : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
    247     if (bytes_written == -1) {
    248       return false;
    249     }
    250     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
    251     ptr += bytes_written;  // Move the buffer forward.
    252     offset += static_cast<size_t>(bytes_written);
    253   }
    254   return true;
    255 }
    256 
    257 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
    258   return WriteFullyGeneric<true>(buffer, byte_count, offset);
    259 }
    260 
    261 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
    262   return WriteFullyGeneric<false>(buffer, byte_count, 0u);
    263 }
    264 
    265 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
    266   DCHECK(!read_only_mode_);
    267   off_t off = static_cast<off_t>(offset);
    268   off_t sz = static_cast<off_t>(size);
    269   if (offset < 0 || static_cast<int64_t>(off) != offset ||
    270       size < 0 || static_cast<int64_t>(sz) != size ||
    271       sz > std::numeric_limits<off_t>::max() - off) {
    272     errno = EINVAL;
    273     return false;
    274   }
    275   if (size == 0) {
    276     return true;
    277   }
    278 #ifdef __linux__
    279   // Use sendfile(), available for files since linux kernel 2.6.33.
    280   off_t end = off + sz;
    281   while (off != end) {
    282     int result = TEMP_FAILURE_RETRY(
    283         sendfile(Fd(), input_file->Fd(), &off, end - off));
    284     if (result == -1) {
    285       return false;
    286     }
    287     // Ignore the number of bytes in `result`, sendfile() already updated `off`.
    288   }
    289 #else
    290   if (lseek(input_file->Fd(), off, SEEK_SET) != off) {
    291     return false;
    292   }
    293   constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize;
    294   const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize);
    295   art::UniqueCPtr<void> buffer(malloc(buffer_size));
    296   if (buffer == nullptr) {
    297     errno = ENOMEM;
    298     return false;
    299   }
    300   while (size != 0) {
    301     size_t chunk_size = std::min<uint64_t>(buffer_size, size);
    302     if (!input_file->ReadFully(buffer.get(), chunk_size) ||
    303         !WriteFully(buffer.get(), chunk_size)) {
    304       return false;
    305     }
    306     size -= chunk_size;
    307   }
    308 #endif
    309   return true;
    310 }
    311 
    312 void FdFile::Erase() {
    313   DCHECK(!read_only_mode_);
    314   TEMP_FAILURE_RETRY(SetLength(0));
    315   TEMP_FAILURE_RETRY(Flush());
    316   TEMP_FAILURE_RETRY(Close());
    317 }
    318 
    319 int FdFile::FlushCloseOrErase() {
    320   DCHECK(!read_only_mode_);
    321   int flush_result = TEMP_FAILURE_RETRY(Flush());
    322   if (flush_result != 0) {
    323     LOG(::art::ERROR) << "CloseOrErase failed while flushing a file.";
    324     Erase();
    325     return flush_result;
    326   }
    327   int close_result = TEMP_FAILURE_RETRY(Close());
    328   if (close_result != 0) {
    329     LOG(::art::ERROR) << "CloseOrErase failed while closing a file.";
    330     Erase();
    331     return close_result;
    332   }
    333   return 0;
    334 }
    335 
    336 int FdFile::FlushClose() {
    337   DCHECK(!read_only_mode_);
    338   int flush_result = TEMP_FAILURE_RETRY(Flush());
    339   if (flush_result != 0) {
    340     LOG(::art::ERROR) << "FlushClose failed while flushing a file.";
    341   }
    342   int close_result = TEMP_FAILURE_RETRY(Close());
    343   if (close_result != 0) {
    344     LOG(::art::ERROR) << "FlushClose failed while closing a file.";
    345   }
    346   return (flush_result != 0) ? flush_result : close_result;
    347 }
    348 
    349 void FdFile::MarkUnchecked() {
    350   guard_state_ = GuardState::kNoCheck;
    351 }
    352 
    353 bool FdFile::ClearContent() {
    354   DCHECK(!read_only_mode_);
    355   if (SetLength(0) < 0) {
    356     PLOG(art::ERROR) << "Failed to reset the length";
    357     return false;
    358   }
    359   return ResetOffset();
    360 }
    361 
    362 bool FdFile::ResetOffset() {
    363   DCHECK(!read_only_mode_);
    364   off_t rc =  TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
    365   if (rc == static_cast<off_t>(-1)) {
    366     PLOG(art::ERROR) << "Failed to reset the offset";
    367     return false;
    368   }
    369   return true;
    370 }
    371 
    372 }  // namespace unix_file
    373