Home | History | Annotate | Download | only in unix_file
      1 /*
      2  * Copyright (C) 2008 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/mapped_file.h"
     18 
     19 #include <fcntl.h>
     20 #include <sys/mman.h>
     21 #include <sys/stat.h>
     22 #include <sys/types.h>
     23 #include <unistd.h>
     24 #include <algorithm>
     25 #include <string>
     26 
     27 #include "base/logging.h"
     28 
     29 namespace unix_file {
     30 
     31 MappedFile::~MappedFile() {
     32 }
     33 
     34 int MappedFile::Close() {
     35   if (IsMapped()) {
     36     Unmap();
     37   }
     38   return FdFile::Close();
     39 }
     40 
     41 bool MappedFile::MapReadOnly() {
     42   CHECK(IsOpened());
     43   CHECK(!IsMapped());
     44 
     45   // Mapping readonly means we don't need to enforce Flush and Close.
     46   resetGuard(GuardState::kNoCheck);
     47 
     48   struct stat st;
     49   int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st));
     50   if (result == -1) {
     51     PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'";
     52     return false;
     53   }
     54   file_size_ = st.st_size;
     55   do {
     56     mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0);
     57   } while (mapped_file_ == MAP_FAILED && errno == EINTR);
     58   if (mapped_file_ == MAP_FAILED) {
     59     PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
     60                   << file_size_ << " bytes to memory";
     61     return false;
     62   }
     63   map_mode_ = kMapReadOnly;
     64   return true;
     65 }
     66 
     67 bool MappedFile::MapReadWrite(int64_t file_size) {
     68   CHECK(IsOpened());
     69   CHECK(!IsMapped());
     70 #ifdef __linux__
     71   int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size));
     72 #else
     73   int result = TEMP_FAILURE_RETRY(ftruncate(Fd(), file_size));
     74 #endif
     75   if (result == -1) {
     76     PLOG(ERROR) << "Failed to truncate file '" << GetPath()
     77                 << "' to size " << file_size;
     78     return false;
     79   }
     80 
     81   // Need to track this now.
     82   resetGuard(GuardState::kBase);
     83 
     84   file_size_ = file_size;
     85   do {
     86     mapped_file_ =
     87         mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0);
     88   } while (mapped_file_ == MAP_FAILED && errno == EINTR);
     89   if (mapped_file_ == MAP_FAILED) {
     90     PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
     91                   << file_size_ << " bytes to memory";
     92     return false;
     93   }
     94   map_mode_ = kMapReadWrite;
     95   return true;
     96 }
     97 
     98 bool MappedFile::Unmap() {
     99   CHECK(IsMapped());
    100   int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_));
    101   if (result == -1) {
    102     PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size "
    103                   << file_size_;
    104     return false;
    105   } else {
    106     mapped_file_ = NULL;
    107     file_size_ = -1;
    108     return true;
    109   }
    110 }
    111 
    112 int64_t MappedFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
    113   if (IsMapped()) {
    114     if (offset < 0) {
    115       errno = EINVAL;
    116       return -errno;
    117     }
    118     int64_t read_size = std::max(static_cast<int64_t>(0),
    119                                  std::min(byte_count, file_size_ - offset));
    120     if (read_size > 0) {
    121       memcpy(buf, data() + offset, read_size);
    122     }
    123     return read_size;
    124   } else {
    125     return FdFile::Read(buf, byte_count, offset);
    126   }
    127 }
    128 
    129 int MappedFile::SetLength(int64_t new_length) {
    130   CHECK(!IsMapped());
    131   return FdFile::SetLength(new_length);
    132 }
    133 
    134 int64_t MappedFile::GetLength() const {
    135   if (IsMapped()) {
    136     return file_size_;
    137   } else {
    138     return FdFile::GetLength();
    139   }
    140 }
    141 
    142 int MappedFile::Flush() {
    143   moveUp(GuardState::kFlushed, "Flushing closed file.");
    144   int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush();
    145   return rc == -1 ? -errno : 0;
    146 }
    147 
    148 int64_t MappedFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
    149   if (IsMapped()) {
    150     CHECK_EQ(kMapReadWrite, map_mode_);
    151     if (offset < 0) {
    152       errno = EINVAL;
    153       return -errno;
    154     }
    155     int64_t write_size = std::max(static_cast<int64_t>(0),
    156                                   std::min(byte_count, file_size_ - offset));
    157     if (write_size > 0) {
    158       memcpy(data() + offset, buf, write_size);
    159       moveTo(GuardState::kBase, GuardState::kClosed, "Writing into a closed file.");
    160     }
    161     return write_size;
    162   } else {
    163     return FdFile::Write(buf, byte_count, offset);
    164   }
    165 }
    166 
    167 int64_t MappedFile::size() const {
    168   return GetLength();
    169 }
    170 
    171 bool MappedFile::IsMapped() const {
    172   return mapped_file_ != NULL && mapped_file_ != MAP_FAILED;
    173 }
    174 
    175 char* MappedFile::data() const {
    176   CHECK(IsMapped());
    177   return static_cast<char*>(mapped_file_);
    178 }
    179 
    180 }  // namespace unix_file
    181