/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "utils/memory/mmap.h" #include <errno.h> #include <fcntl.h> #include <stdint.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include "utils/base/logging.h" #include "utils/base/macros.h" namespace libtextclassifier3 { namespace { inline std::string GetLastSystemError() { return std::string(strerror(errno)); } inline MmapHandle GetErrorMmapHandle() { return MmapHandle(nullptr, 0); } class FileCloser { public: explicit FileCloser(int fd) : fd_(fd) {} ~FileCloser() { int result = close(fd_); if (result != 0) { const std::string last_error = GetLastSystemError(); TC3_LOG(ERROR) << "Error closing file descriptor: " << last_error; } } private: const int fd_; TC3_DISALLOW_COPY_AND_ASSIGN(FileCloser); }; } // namespace MmapHandle MmapFile(const std::string &filename) { int fd = open(filename.c_str(), O_RDONLY); if (fd < 0) { const std::string last_error = GetLastSystemError(); TC3_LOG(ERROR) << "Error opening " << filename << ": " << last_error; return GetErrorMmapHandle(); } // Make sure we close fd no matter how we exit this function. As the man page // for mmap clearly states: "closing the file descriptor does not unmap the // region." Hence, we can close fd as soon as we return from here. FileCloser file_closer(fd); return MmapFile(fd); } MmapHandle MmapFile(int fd) { // Get file stats to obtain file size. struct stat sb; if (fstat(fd, &sb) != 0) { const std::string last_error = GetLastSystemError(); TC3_LOG(ERROR) << "Unable to stat fd: " << last_error; return GetErrorMmapHandle(); } return MmapFile(fd, /*segment_offset=*/0, /*segment_size=*/sb.st_size); } MmapHandle MmapFile(int fd, int64 segment_offset, int64 segment_size) { static const int64 kPageSize = sysconf(_SC_PAGE_SIZE); const int64 aligned_offset = (segment_offset / kPageSize) * kPageSize; const int64 alignment_shift = segment_offset - aligned_offset; const int64 aligned_length = segment_size + alignment_shift; // Perform actual mmap. void *mmap_addr = mmap( // Let system pick address for mmapp-ed data. nullptr, aligned_length, // One can read / write the mapped data (but see MAP_PRIVATE below). // Normally, we expect only to read it, but in the future, we may want to // write it, to fix e.g., endianness differences. PROT_READ | PROT_WRITE, // Updates to mmaped data are *not* propagated to actual file. // AFAIK(salcianu) that's anyway not possible on Android. MAP_PRIVATE, // Descriptor of file to mmap. fd, aligned_offset); if (mmap_addr == MAP_FAILED) { const std::string last_error = GetLastSystemError(); TC3_LOG(ERROR) << "Error while mmapping: " << last_error; return GetErrorMmapHandle(); } return MmapHandle(static_cast<char *>(mmap_addr) + alignment_shift, segment_size, /*unmap_addr=*/mmap_addr); } bool Unmap(MmapHandle mmap_handle) { if (!mmap_handle.ok()) { // Unmapping something that hasn't been mapped is trivially successful. return true; } if (munmap(mmap_handle.unmap_addr(), mmap_handle.num_bytes()) != 0) { const std::string last_error = GetLastSystemError(); TC3_LOG(ERROR) << "Error during Unmap / munmap: " << last_error; return false; } return true; } } // namespace libtextclassifier3