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