xref: /aosp_15_r20/external/libtextclassifier/native/lang_id/common/file/mmap.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1 /*
2  * Copyright (C) 2018 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 "lang_id/common/file/mmap.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdint.h>
22 #include <string.h>
23 #ifdef _WIN32
24 #include <winbase.h>
25 #include <windows.h>
26 #else
27 #include <sys/mman.h>
28 #include <unistd.h>
29 #endif
30 #include <sys/stat.h>
31 
32 #include <string>
33 
34 #include "lang_id/common/lite_base/logging.h"
35 #include "lang_id/common/lite_base/macros.h"
36 
37 namespace libtextclassifier3 {
38 namespace mobile {
39 
40 namespace {
GetErrorMmapHandle()41 inline MmapHandle GetErrorMmapHandle() { return MmapHandle(nullptr, 0); }
42 }  // anonymous namespace
43 
44 #ifdef _WIN32
45 
46 namespace {
GetLastSystemError()47 inline std::string GetLastSystemError() {
48   LPTSTR message_buffer;
49   DWORD error_code = GetLastError();
50   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
51                     FORMAT_MESSAGE_IGNORE_INSERTS,
52                 NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
53                 (LPTSTR)&message_buffer, 0, NULL);
54   std::string result(message_buffer);
55   LocalFree(message_buffer);
56   return result;
57 }
58 
59 // Class for automatically closing a Win32 HANDLE on exit from a scope.
60 class Win32HandleCloser {
61  public:
Win32HandleCloser(HANDLE handle)62   explicit Win32HandleCloser(HANDLE handle) : handle_(handle) {}
~Win32HandleCloser()63   ~Win32HandleCloser() {
64     bool result = CloseHandle(handle_);
65     if (!result) {
66       const DWORD last_error = GetLastError();
67       SAFTM_LOG(ERROR) << "Error closing handle: " << last_error << ": "
68                        << GetLastSystemError();
69     }
70   }
71 
72  private:
73   const HANDLE handle_;
74 
75   SAFTM_DISALLOW_COPY_AND_ASSIGN(Win32HandleCloser);
76 };
77 }  // namespace
78 
MmapFile(const std::string & filename)79 MmapHandle MmapFile(const std::string &filename) {
80   HANDLE handle =
81       CreateFile(filename.c_str(),  // File to open.
82                  GENERIC_READ,      // Open for reading.
83                  FILE_SHARE_READ,   // Share for reading.
84                  NULL,              // Default security.
85                  OPEN_EXISTING,     // Existing file only.
86                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,  // Normal file.
87                  NULL);  // No attr. template.
88   if (handle == INVALID_HANDLE_VALUE) {
89     const std::string last_error = GetLastSystemError();
90     SAFTM_LOG(ERROR) << "Error opening " << filename << ": " << last_error;
91     return GetErrorMmapHandle();
92   }
93 
94   // Make sure we close handle no matter how we exit this function.
95   Win32HandleCloser handle_closer(handle);
96 
97   return MmapFile(handle);
98 }
99 
MmapFile(HANDLE file_handle)100 MmapHandle MmapFile(HANDLE file_handle) {
101   // Get the file size.
102   DWORD file_size_high = 0;
103   DWORD file_size_low = GetFileSize(file_handle, &file_size_high);
104   if (file_size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
105     const std::string last_error = GetLastSystemError();
106     SAFTM_LOG(ERROR) << "Unable to stat fd: " << last_error;
107     return GetErrorMmapHandle();
108   }
109   size_t file_size_in_bytes = (static_cast<size_t>(file_size_high) << 32) +
110                               static_cast<size_t>(file_size_low);
111 
112   // Create a file mapping object that refers to the file.
113   HANDLE file_mapping_object =
114       CreateFileMappingA(file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
115   if (file_mapping_object == NULL) {
116     const std::string last_error = GetLastSystemError();
117     SAFTM_LOG(ERROR) << "Error while mmapping: " << last_error;
118     return GetErrorMmapHandle();
119   }
120   Win32HandleCloser handle_closer(file_mapping_object);
121 
122   // Map the file mapping object into memory.
123   void *mmap_addr =
124       MapViewOfFile(file_mapping_object, FILE_MAP_READ, 0, 0,  // File offset.
125                     0  // Number of bytes to map; 0 means map the whole file.
126       );
127   if (mmap_addr == nullptr) {
128     const std::string last_error = GetLastSystemError();
129     SAFTM_LOG(ERROR) << "Error while mmapping: " << last_error;
130     return GetErrorMmapHandle();
131   }
132 
133   return MmapHandle(mmap_addr, file_size_in_bytes);
134 }
135 
Unmap(MmapHandle mmap_handle)136 bool Unmap(MmapHandle mmap_handle) {
137   if (!mmap_handle.ok()) {
138     // Unmapping something that hasn't been mapped is trivially successful.
139     return true;
140   }
141   bool succeeded = UnmapViewOfFile(mmap_handle.start());
142   if (!succeeded) {
143     const std::string last_error = GetLastSystemError();
144     SAFTM_LOG(ERROR) << "Error during Unmap / UnmapViewOfFile: " << last_error;
145     return false;
146   }
147   return true;
148 }
149 
150 #else
151 
152 namespace {
GetLastSystemError()153 inline std::string GetLastSystemError() { return std::string(strerror(errno)); }
154 
155 class FileCloser {
156  public:
FileCloser(int fd)157   explicit FileCloser(int fd) : fd_(fd) {}
~FileCloser()158   ~FileCloser() {
159     int result = close(fd_);
160     if (result != 0) {
161       const std::string last_error = GetLastSystemError();
162       SAFTM_LOG(ERROR) << "Error closing file descriptor: " << last_error;
163     }
164   }
165 
166  private:
167   const int fd_;
168 
169   SAFTM_DISALLOW_COPY_AND_ASSIGN(FileCloser);
170 };
171 }  // namespace
172 
MmapFile(const std::string & filename)173 MmapHandle MmapFile(const std::string &filename) {
174   int fd = open(filename.c_str(), O_RDONLY);
175 
176   if (fd < 0) {
177     const std::string last_error = GetLastSystemError();
178     SAFTM_LOG(ERROR) << "Error opening " << filename << ": " << last_error;
179     return GetErrorMmapHandle();
180   }
181 
182   // Make sure we close fd no matter how we exit this function.  As the man page
183   // for mmap clearly states: "closing the file descriptor does not unmap the
184   // region."  Hence, we can close fd as soon as we return from here.
185   FileCloser file_closer(fd);
186 
187   return MmapFile(fd);
188 }
189 
MmapFile(int fd)190 MmapHandle MmapFile(int fd) {
191   // Get file stats to obtain file size.
192   struct stat sb;
193   if (fstat(fd, &sb) != 0) {
194     const std::string last_error = GetLastSystemError();
195     SAFTM_LOG(ERROR) << "Unable to stat fd: " << last_error;
196     return GetErrorMmapHandle();
197   }
198   size_t file_size_in_bytes = static_cast<size_t>(sb.st_size);
199 
200   // Perform actual mmap.
201   return MmapFile(fd, /*offset_in_bytes=*/0, file_size_in_bytes);
202 }
203 
MmapFile(int fd,size_t offset_in_bytes,size_t size_in_bytes)204 MmapHandle MmapFile(int fd, size_t offset_in_bytes, size_t size_in_bytes) {
205   // Make sure the offset is a multiple of the page size, as returned by
206   // sysconf(_SC_PAGE_SIZE); this is required by the man-page for mmap.
207   static const size_t kPageSize = sysconf(_SC_PAGE_SIZE);
208   const size_t aligned_offset = (offset_in_bytes / kPageSize) * kPageSize;
209   const size_t alignment_shift = offset_in_bytes - aligned_offset;
210   const size_t aligned_length = size_in_bytes + alignment_shift;
211 
212   void *mmap_addr = mmap(
213 
214       // Let system pick address for mmapp-ed data.
215       nullptr,
216 
217       aligned_length,
218 
219       // One can read / write the mapped data (but see MAP_PRIVATE below).
220       // Normally, we expect only to read it, but in the future, we may want to
221       // write it, to fix e.g., endianness differences.
222       PROT_READ | PROT_WRITE,
223 
224       // Updates to mmaped data are *not* propagated to actual file.
225       // AFAIK(salcianu) that's anyway not possible on Android.
226       MAP_PRIVATE,
227 
228       // Descriptor of file to mmap.
229       fd,
230 
231       aligned_offset);
232   if (mmap_addr == MAP_FAILED) {
233     const std::string last_error = GetLastSystemError();
234     SAFTM_LOG(ERROR) << "Error while mmapping: " << last_error;
235     return GetErrorMmapHandle();
236   }
237 
238   return MmapHandle(static_cast<char *>(mmap_addr) + alignment_shift,
239                     size_in_bytes);
240 }
241 
Unmap(MmapHandle mmap_handle)242 bool Unmap(MmapHandle mmap_handle) {
243   if (!mmap_handle.ok()) {
244     // Unmapping something that hasn't been mapped is trivially successful.
245     return true;
246   }
247   if (munmap(mmap_handle.start(), mmap_handle.num_bytes()) != 0) {
248     const std::string last_error = GetLastSystemError();
249     SAFTM_LOG(ERROR) << "Error during Unmap / munmap: " << last_error;
250     return false;
251   }
252   return true;
253 }
254 
255 #endif
256 
257 }  // namespace mobile
258 }  // namespace nlp_saft
259