tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

mmap.c (6475B)


      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) || ... */