mmap.c 6.3 KB

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