Tor 0.4.9.0-alpha-dev
mmap.c
Go to the documentation of this file.
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"
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. */
46tor_mmap_file,(const char *filename))
47{
48 int fd; /* router file */
49 char *string;
50 int result;
51 tor_mmap_t *res;
52 size_t size, filesize;
53 struct stat st;
54
55 tor_assert(filename);
56
57 fd = tor_open_cloexec(filename, O_RDONLY, 0);
58 if (fd<0) {
59 int save_errno = errno;
60 int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
61 log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
62 strerror(errno));
63 errno = save_errno;
64 return NULL;
65 }
66
67 /* Get the size of the file */
68 result = fstat(fd, &st);
69 if (result != 0) {
70 int save_errno = errno;
71 log_warn(LD_FS,
72 "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
73 filename, strerror(errno));
74 close(fd);
75 errno = save_errno;
76 return NULL;
77 }
78 size = filesize = (size_t)(st.st_size);
79
80 if (st.st_size > SSIZE_T_CEILING || (off_t)size < st.st_size) {
81 log_warn(LD_FS, "File \"%s\" is too large. Ignoring.",filename);
82 errno = EFBIG;
83 close(fd);
84 return NULL;
85 }
86 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 log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
90 errno = ERANGE;
91 close(fd);
92 return NULL;
93 }
94
95 string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
96 close(fd);
97 if (string == MAP_FAILED) {
98 int save_errno = errno;
99 log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
100 strerror(errno));
101 errno = save_errno;
102 return NULL;
103 }
104
105 res = tor_malloc_zero(sizeof(tor_mmap_t));
106 res->data = string;
107 res->size = filesize;
108 res->mapping_size = size;
109
110 return res;
111}
112/** Release storage held for a memory mapping; returns 0 on success,
113 * or -1 on failure (and logs a warning). */
114MOCK_IMPL(int,
115tor_munmap_file,(tor_mmap_t *handle))
116{
117 int res;
118
119 if (handle == NULL)
120 return 0;
121
122 res = munmap((char*)handle->data, handle->mapping_size);
123 if (res == 0) {
124 /* munmap() succeeded */
125 tor_free(handle);
126 } else {
127 log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
128 strerror(errno));
129 res = -1;
130 }
131
132 return res;
133}
134#elif defined(_WIN32)
136tor_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) ?
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 */
216MOCK_IMPL(int,
217tor_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) || ... */
Header for compat_string.c.
Wrappers for reading and writing data to files on disk.
int tor_open_cloexec(const char *path, int flags, unsigned mode)
Definition: files.c:54
Headers for log.c.
#define log_fn(severity, domain, args,...)
Definition: log.h:283
#define LD_FS
Definition: log.h:70
#define LOG_WARN
Definition: log.h:53
#define LOG_INFO
Definition: log.h:45
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:56
Header for mmap.c.
size_t size
Definition: mmap.h:27
const char * data
Definition: mmap.h:26
#define MOCK_IMPL(rv, funcname, arglist)
Definition: testsupport.h:133
#define SSIZE_T_CEILING
Definition: torint.h:124
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:103
Header for win32err.c.