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