LCOV - code coverage report
Current view: top level - lib/fs - mmap.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 38 53 71.7 %
Date: 2021-11-24 03:28:48 Functions: 2 2 100.0 %

          Line data    Source code
       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         165 : MOCK_IMPL(tor_mmap_t *,
      46             : tor_mmap_file,(const char *filename))
      47             : {
      48         165 :   int fd; /* router file */
      49         165 :   char *string;
      50         165 :   int result;
      51         165 :   tor_mmap_t *res;
      52         165 :   size_t size, filesize;
      53         165 :   struct stat st;
      54             : 
      55         165 :   tor_assert(filename);
      56             : 
      57         165 :   fd = tor_open_cloexec(filename, O_RDONLY, 0);
      58         165 :   if (fd<0) {
      59          19 :     int save_errno = errno;
      60          19 :     int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
      61          19 :     log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
      62             :            strerror(errno));
      63          19 :     errno = save_errno;
      64          19 :     return NULL;
      65             :   }
      66             : 
      67             :   /* Get the size of the file */
      68         146 :   result = fstat(fd, &st);
      69         146 :   if (result != 0) {
      70           0 :     int save_errno = errno;
      71           0 :     log_warn(LD_FS,
      72             :              "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
      73             :              filename, strerror(errno));
      74           0 :     close(fd);
      75           0 :     errno = save_errno;
      76           0 :     return NULL;
      77             :   }
      78         146 :   size = filesize = (size_t)(st.st_size);
      79             : 
      80         146 :   if (st.st_size > SSIZE_T_CEILING || (off_t)size < st.st_size) {
      81           0 :     log_warn(LD_FS, "File \"%s\" is too large. Ignoring.",filename);
      82           0 :     errno = EFBIG;
      83           0 :     close(fd);
      84           0 :     return NULL;
      85             :   }
      86         146 :   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           1 :     log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
      90           1 :     errno = ERANGE;
      91           1 :     close(fd);
      92           1 :     return NULL;
      93             :   }
      94             : 
      95         145 :   string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
      96         145 :   close(fd);
      97         145 :   if (string == MAP_FAILED) {
      98           0 :     int save_errno = errno;
      99           0 :     log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
     100             :              strerror(errno));
     101           0 :     errno = save_errno;
     102           0 :     return NULL;
     103             :   }
     104             : 
     105         145 :   res = tor_malloc_zero(sizeof(tor_mmap_t));
     106         145 :   res->data = string;
     107         145 :   res->size = filesize;
     108         145 :   res->mapping_size = size;
     109             : 
     110         145 :   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         140 : MOCK_IMPL(int,
     115             : tor_munmap_file,(tor_mmap_t *handle))
     116             : {
     117         140 :   int res;
     118             : 
     119         140 :   if (handle == NULL)
     120             :     return 0;
     121             : 
     122         139 :   res = munmap((char*)handle->data, handle->mapping_size);
     123         139 :   if (res == 0) {
     124             :     /* munmap() succeeded */
     125         139 :     tor_free(handle);
     126             :   } else {
     127           0 :     log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
     128             :              strerror(errno));
     129           0 :     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) || ... */

Generated by: LCOV version 1.14