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/logging.h"
     18 #include "base/unix_file/mapped_file.h"
     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 namespace unix_file {
     28 
     29 MappedFile::~MappedFile() {
     30 }
     31 
     32 int MappedFile::Close() {
     33   if (IsMapped()) {
     34     Unmap();
     35   }
     36   return FdFile::Close();
     37 }
     38 
     39 bool MappedFile::MapReadOnly() {
     40   CHECK(IsOpened());
     41   CHECK(!IsMapped());
     42   struct stat st;
     43   int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st));
     44   if (result == -1) {
     45     PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'";
     46     return false;
     47   }
     48   file_size_ = st.st_size;
     49   do {
     50     mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0);
     51   } while (mapped_file_ == MAP_FAILED && errno == EINTR);
     52   if (mapped_file_ == MAP_FAILED) {
     53     PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
     54                   << file_size_ << " bytes to memory";
     55     return false;
     56   }
     57   map_mode_ = kMapReadOnly;
     58   return true;
     59 }
     60 
     61 bool MappedFile::MapReadWrite(int64_t file_size) {
     62   CHECK(IsOpened());
     63   CHECK(!IsMapped());
     64   int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size));
     65   if (result == -1) {
     66     PLOG(ERROR) << "Failed to truncate file '" << GetPath()
     67                 << "' to size " << file_size;
     68     return false;
     69   }
     70   file_size_ = file_size;
     71   do {
     72     mapped_file_ =
     73         mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0);
     74   } while (mapped_file_ == MAP_FAILED && errno == EINTR);
     75   if (mapped_file_ == MAP_FAILED) {
     76     PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
     77                   << file_size_ << " bytes to memory";
     78     return false;
     79   }
     80   map_mode_ = kMapReadWrite;
     81   return true;
     82 }
     83 
     84 bool MappedFile::Unmap() {
     85   CHECK(IsMapped());
     86   int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_));
     87   if (result == -1) {
     88     PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size "
     89                   << file_size_;
     90     return false;
     91   } else {
     92     mapped_file_ = NULL;
     93     file_size_ = -1;
     94     return true;
     95   }
     96 }
     97 
     98 int64_t MappedFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
     99   if (IsMapped()) {
    100     if (offset < 0) {
    101       errno = EINVAL;
    102       return -errno;
    103     }
    104     int64_t read_size = std::max(0LL, std::min(byte_count, file_size_ - offset));
    105     if (read_size > 0) {
    106       memcpy(buf, data() + offset, read_size);
    107     }
    108     return read_size;
    109   } else {
    110     return FdFile::Read(buf, byte_count, offset);
    111   }
    112 }
    113 
    114 int MappedFile::SetLength(int64_t new_length) {
    115   CHECK(!IsMapped());
    116   return FdFile::SetLength(new_length);
    117 }
    118 
    119 int64_t MappedFile::GetLength() const {
    120   if (IsMapped()) {
    121     return file_size_;
    122   } else {
    123     return FdFile::GetLength();
    124   }
    125 }
    126 
    127 int MappedFile::Flush() {
    128   int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush();
    129   return rc == -1 ? -errno : 0;
    130 }
    131 
    132 int64_t MappedFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
    133   if (IsMapped()) {
    134     CHECK_EQ(kMapReadWrite, map_mode_);
    135     if (offset < 0) {
    136       errno = EINVAL;
    137       return -errno;
    138     }
    139     int64_t write_size = std::max(0LL, std::min(byte_count, file_size_ - offset));
    140     if (write_size > 0) {
    141       memcpy(data() + offset, buf, write_size);
    142     }
    143     return write_size;
    144   } else {
    145     return FdFile::Write(buf, byte_count, offset);
    146   }
    147 }
    148 
    149 int64_t MappedFile::size() const {
    150   return GetLength();
    151 }
    152 
    153 bool MappedFile::IsMapped() const {
    154   return mapped_file_ != NULL && mapped_file_ != MAP_FAILED;
    155 }
    156 
    157 char* MappedFile::data() const {
    158   CHECK(IsMapped());
    159   return static_cast<char*>(mapped_file_);
    160 }
    161 
    162 }  // namespace unix_file
    163