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