huangcm
2025-02-28 b45e871a67cd1272e3da9ba5bd383f832b0f1824
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * 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