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 MOCK_IMPL(tor_mmap_t *, 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) 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) || ... */ 241