Tor  0.4.7.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. */
46 tor_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). */
114 MOCK_IMPL(int,
115 tor_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)
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) || ... */
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:52
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:102
Header for win32err.c.