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