Line data Source code
1 : /* Copyright (c) 2003-2004, Roger Dingledine
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file mmap.c
8 : *
9 : * \brief Cross-platform support for mapping files into our address space.
10 : **/
11 :
12 : #include "lib/fs/mmap.h"
13 : #include "lib/fs/files.h"
14 : #include "lib/log/log.h"
15 : #include "lib/log/util_bug.h"
16 : #include "lib/log/win32err.h"
17 : #include "lib/string/compat_string.h"
18 : #include "lib/malloc/malloc.h"
19 :
20 : #ifdef HAVE_MMAP
21 : #include <sys/mman.h>
22 : #endif
23 : #ifdef HAVE_SYS_STAT_H
24 : #include <sys/stat.h>
25 : #endif
26 : #ifdef HAVE_UNISTD_H
27 : #include <unistd.h>
28 : #endif
29 : #ifdef HAVE_FCNTL_H
30 : #include <fcntl.h>
31 : #endif
32 :
33 : #ifdef _WIN32
34 : #include <windows.h>
35 : #endif
36 :
37 : #include <errno.h>
38 : #include <string.h>
39 :
40 : #if defined(HAVE_MMAP) || defined(RUNNING_DOXYGEN)
41 : /** Try to create a memory mapping for <b>filename</b> and return it. On
42 : * failure, return NULL. Sets errno properly, using ERANGE to mean
43 : * "empty file". Must only be called on trusted Tor-owned files, as changing
44 : * the underlying file's size causes unspecified behavior. */
45 165 : MOCK_IMPL(tor_mmap_t *,
46 : tor_mmap_file,(const char *filename))
47 : {
48 165 : int fd; /* router file */
49 165 : char *string;
50 165 : int result;
51 165 : tor_mmap_t *res;
52 165 : size_t size, filesize;
53 165 : struct stat st;
54 :
55 165 : tor_assert(filename);
56 :
57 165 : fd = tor_open_cloexec(filename, O_RDONLY, 0);
58 165 : if (fd<0) {
59 19 : int save_errno = errno;
60 19 : int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
61 19 : log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
62 : strerror(errno));
63 19 : errno = save_errno;
64 19 : return NULL;
65 : }
66 :
67 : /* Get the size of the file */
68 146 : result = fstat(fd, &st);
69 146 : if (result != 0) {
70 0 : int save_errno = errno;
71 0 : log_warn(LD_FS,
72 : "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
73 : filename, strerror(errno));
74 0 : close(fd);
75 0 : errno = save_errno;
76 0 : return NULL;
77 : }
78 146 : size = filesize = (size_t)(st.st_size);
79 :
80 146 : if (st.st_size > SSIZE_T_CEILING || (off_t)size < st.st_size) {
81 0 : log_warn(LD_FS, "File \"%s\" is too large. Ignoring.",filename);
82 0 : errno = EFBIG;
83 0 : close(fd);
84 0 : return NULL;
85 : }
86 146 : if (!size) {
87 : /* Zero-length file. If we call mmap on it, it will succeed but
88 : * return NULL, and bad things will happen. So just fail. */
89 1 : log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
90 1 : errno = ERANGE;
91 1 : close(fd);
92 1 : return NULL;
93 : }
94 :
95 145 : string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
96 145 : close(fd);
97 145 : if (string == MAP_FAILED) {
98 0 : int save_errno = errno;
99 0 : log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
100 : strerror(errno));
101 0 : errno = save_errno;
102 0 : return NULL;
103 : }
104 :
105 145 : res = tor_malloc_zero(sizeof(tor_mmap_t));
106 145 : res->data = string;
107 145 : res->size = filesize;
108 145 : res->mapping_size = size;
109 :
110 145 : return res;
111 : }
112 : /** Release storage held for a memory mapping; returns 0 on success,
113 : * or -1 on failure (and logs a warning). */
114 140 : MOCK_IMPL(int,
115 : tor_munmap_file,(tor_mmap_t *handle))
116 : {
117 140 : int res;
118 :
119 140 : if (handle == NULL)
120 : return 0;
121 :
122 139 : res = munmap((char*)handle->data, handle->mapping_size);
123 139 : if (res == 0) {
124 : /* munmap() succeeded */
125 139 : tor_free(handle);
126 : } else {
127 0 : log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
128 : strerror(errno));
129 0 : res = -1;
130 : }
131 :
132 : return res;
133 : }
134 : #elif defined(_WIN32)
135 : MOCK_IMPL(tor_mmap_t *,
136 : tor_mmap_file,(const char *filename))
137 : {
138 : TCHAR tfilename[MAX_PATH]= {0};
139 : tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
140 : int empty = 0;
141 : HANDLE file_handle = INVALID_HANDLE_VALUE;
142 : DWORD size_low, size_high;
143 : uint64_t real_size;
144 : res->mmap_handle = NULL;
145 : #ifdef UNICODE
146 : mbstowcs(tfilename,filename,MAX_PATH);
147 : #else
148 : strlcpy(tfilename,filename,MAX_PATH);
149 : #endif
150 : file_handle = CreateFile(tfilename,
151 : GENERIC_READ, FILE_SHARE_READ,
152 : NULL,
153 : OPEN_EXISTING,
154 : FILE_ATTRIBUTE_NORMAL,
155 : 0);
156 :
157 : if (file_handle == INVALID_HANDLE_VALUE)
158 : goto win_err;
159 :
160 : size_low = GetFileSize(file_handle, &size_high);
161 :
162 : if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
163 : log_warn(LD_FS,"Error getting size of \"%s\".",filename);
164 : goto win_err;
165 : }
166 : if (size_low == 0 && size_high == 0) {
167 : log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
168 : empty = 1;
169 : goto err;
170 : }
171 : real_size = (((uint64_t)size_high)<<32) | size_low;
172 : if (real_size > SIZE_MAX) {
173 : log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename);
174 : goto err;
175 : }
176 : res->size = real_size;
177 :
178 : res->mmap_handle = CreateFileMapping(file_handle,
179 : NULL,
180 : PAGE_READONLY,
181 : size_high,
182 : size_low,
183 : NULL);
184 : if (res->mmap_handle == NULL)
185 : goto win_err;
186 : res->data = (char*) MapViewOfFile(res->mmap_handle,
187 : FILE_MAP_READ,
188 : 0, 0, 0);
189 : if (!res->data)
190 : goto win_err;
191 :
192 : CloseHandle(file_handle);
193 : return res;
194 : win_err: {
195 : DWORD e = GetLastError();
196 : int severity = (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) ?
197 : LOG_INFO : LOG_WARN;
198 : char *msg = format_win32_error(e);
199 : log_fn(severity, LD_FS, "Couldn't mmap file \"%s\": %s", filename, msg);
200 : tor_free(msg);
201 : if (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND)
202 : errno = ENOENT;
203 : else
204 : errno = EINVAL;
205 : }
206 : err:
207 : if (empty)
208 : errno = ERANGE;
209 : if (file_handle != INVALID_HANDLE_VALUE)
210 : CloseHandle(file_handle);
211 : tor_munmap_file(res);
212 : return NULL;
213 : }
214 :
215 : /* Unmap the file, and return 0 for success or -1 for failure */
216 : MOCK_IMPL(int,
217 : tor_munmap_file,(tor_mmap_t *handle))
218 : {
219 : if (handle == NULL)
220 : return 0;
221 :
222 : if (handle->data) {
223 : /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
224 : have to be redefined as non-const. */
225 : BOOL ok = UnmapViewOfFile( (LPVOID) handle->data);
226 : if (!ok) {
227 : log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d",
228 : (int)GetLastError());
229 : }
230 : }
231 :
232 : if (handle->mmap_handle != NULL)
233 : CloseHandle(handle->mmap_handle);
234 : tor_free(handle);
235 :
236 : return 0;
237 : }
238 : #else
239 : #error "cannot implement tor_mmap_file"
240 : #endif /* defined(HAVE_MMAP) || defined(RUNNING_DOXYGEN) || ... */
|