1/* 2 * Copyright (C) 2017 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 "util/memory/mmap.h" 18 19#include <errno.h> 20#include <fcntl.h> 21#include <stdint.h> 22#include <string.h> 23#include <sys/mman.h> 24#include <sys/stat.h> 25#include <unistd.h> 26 27#include "util/base/logging.h" 28#include "util/base/macros.h" 29 30namespace libtextclassifier2 { 31 32namespace { 33inline std::string GetLastSystemError() { return std::string(strerror(errno)); } 34 35inline MmapHandle GetErrorMmapHandle() { return MmapHandle(nullptr, 0); } 36 37class FileCloser { 38 public: 39 explicit FileCloser(int fd) : fd_(fd) {} 40 ~FileCloser() { 41 int result = close(fd_); 42 if (result != 0) { 43 const std::string last_error = GetLastSystemError(); 44 TC_LOG(ERROR) << "Error closing file descriptor: " << last_error; 45 } 46 } 47 48 private: 49 const int fd_; 50 51 TC_DISALLOW_COPY_AND_ASSIGN(FileCloser); 52}; 53 54} // namespace 55 56MmapHandle MmapFile(const std::string &filename) { 57 int fd = open(filename.c_str(), O_RDONLY); 58 59 if (fd < 0) { 60 const std::string last_error = GetLastSystemError(); 61 TC_LOG(ERROR) << "Error opening " << filename << ": " << last_error; 62 return GetErrorMmapHandle(); 63 } 64 65 // Make sure we close fd no matter how we exit this function. As the man page 66 // for mmap clearly states: "closing the file descriptor does not unmap the 67 // region." Hence, we can close fd as soon as we return from here. 68 FileCloser file_closer(fd); 69 70 return MmapFile(fd); 71} 72 73MmapHandle MmapFile(int fd) { 74 // Get file stats to obtain file size. 75 struct stat sb; 76 if (fstat(fd, &sb) != 0) { 77 const std::string last_error = GetLastSystemError(); 78 TC_LOG(ERROR) << "Unable to stat fd: " << last_error; 79 return GetErrorMmapHandle(); 80 } 81 82 return MmapFile(fd, /*segment_offset=*/0, /*segment_size=*/sb.st_size); 83} 84 85MmapHandle MmapFile(int fd, int64 segment_offset, int64 segment_size) { 86 static const int64 kPageSize = sysconf(_SC_PAGE_SIZE); 87 const int64 aligned_offset = (segment_offset / kPageSize) * kPageSize; 88 const int64 alignment_shift = segment_offset - aligned_offset; 89 const int64 aligned_length = segment_size + alignment_shift; 90 91 // Perform actual mmap. 92 void *mmap_addr = mmap( 93 94 // Let system pick address for mmapp-ed data. 95 nullptr, 96 97 aligned_length, 98 99 // One can read / write the mapped data (but see MAP_PRIVATE below). 100 // Normally, we expect only to read it, but in the future, we may want to 101 // write it, to fix e.g., endianness differences. 102 PROT_READ | PROT_WRITE, 103 104 // Updates to mmaped data are *not* propagated to actual file. 105 // AFAIK(salcianu) that's anyway not possible on Android. 106 MAP_PRIVATE, 107 108 // Descriptor of file to mmap. 109 fd, 110 111 aligned_offset); 112 if (mmap_addr == MAP_FAILED) { 113 const std::string last_error = GetLastSystemError(); 114 TC_LOG(ERROR) << "Error while mmapping: " << last_error; 115 return GetErrorMmapHandle(); 116 } 117 118 return MmapHandle(static_cast<char *>(mmap_addr) + alignment_shift, 119 segment_size, /*unmap_addr=*/mmap_addr); 120} 121 122bool Unmap(MmapHandle mmap_handle) { 123 if (!mmap_handle.ok()) { 124 // Unmapping something that hasn't been mapped is trivially successful. 125 return true; 126 } 127 if (munmap(mmap_handle.unmap_addr(), mmap_handle.num_bytes()) != 0) { 128 const std::string last_error = GetLastSystemError(); 129 TC_LOG(ERROR) << "Error during Unmap / munmap: " << last_error; 130 return false; 131 } 132 return true; 133} 134 135} // namespace libtextclassifier2 136