LCOV - code coverage report
Current view: top level - Modules - mmapmodule.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit 5e6661bce9] Lines: 46 517 8.9 %
Date: 2023-03-20 08:15:36 Functions: 3 35 8.6 %
Branches: 36 400 9.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  /  Author: Sam Rushing <rushing@nightmare.com>
       3                 :            :  /  Hacked for Unix by AMK
       4                 :            :  /  $Id$
       5                 :            : 
       6                 :            :  / Modified to support mmap with offset - to map a 'window' of a file
       7                 :            :  /   Author:  Yotam Medini  yotamm@mellanox.co.il
       8                 :            :  /
       9                 :            :  / mmapmodule.cpp -- map a view of a file into memory
      10                 :            :  /
      11                 :            :  / todo: need permission flags, perhaps a 'chsize' analog
      12                 :            :  /   not all functions check range yet!!!
      13                 :            :  /
      14                 :            :  /
      15                 :            :  / This version of mmapmodule.c has been changed significantly
      16                 :            :  / from the original mmapfile.c on which it was based.
      17                 :            :  / The original version of mmapfile is maintained by Sam at
      18                 :            :  / ftp://squirl.nightmare.com/pub/python/python-ext.
      19                 :            : */
      20                 :            : 
      21                 :            : #ifndef Py_BUILD_CORE_BUILTIN
      22                 :            : #  define Py_BUILD_CORE_MODULE 1
      23                 :            : #endif
      24                 :            : 
      25                 :            : #define PY_SSIZE_T_CLEAN
      26                 :            : #include <Python.h>
      27                 :            : #include "pycore_bytesobject.h"   // _PyBytes_Find()
      28                 :            : #include "pycore_fileutils.h"     // _Py_stat_struct
      29                 :            : #include "structmember.h"         // PyMemberDef
      30                 :            : #include <stddef.h>               // offsetof()
      31                 :            : 
      32                 :            : // to support MS_WINDOWS_SYSTEM OpenFileMappingA / CreateFileMappingA
      33                 :            : // need to be replaced with OpenFileMappingW / CreateFileMappingW
      34                 :            : #if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_GAMES)
      35                 :            : 
      36                 :            : #ifndef MS_WINDOWS
      37                 :            : #define UNIX
      38                 :            : # ifdef HAVE_FCNTL_H
      39                 :            : #  include <fcntl.h>
      40                 :            : # endif /* HAVE_FCNTL_H */
      41                 :            : #endif
      42                 :            : 
      43                 :            : #ifdef MS_WINDOWS
      44                 :            : #include <windows.h>
      45                 :            : static int
      46                 :            : my_getpagesize(void)
      47                 :            : {
      48                 :            :     SYSTEM_INFO si;
      49                 :            :     GetSystemInfo(&si);
      50                 :            :     return si.dwPageSize;
      51                 :            : }
      52                 :            : 
      53                 :            : static int
      54                 :            : my_getallocationgranularity (void)
      55                 :            : {
      56                 :            : 
      57                 :            :     SYSTEM_INFO si;
      58                 :            :     GetSystemInfo(&si);
      59                 :            :     return si.dwAllocationGranularity;
      60                 :            : }
      61                 :            : 
      62                 :            : #endif
      63                 :            : 
      64                 :            : #ifdef UNIX
      65                 :            : #include <sys/mman.h>
      66                 :            : #include <sys/stat.h>
      67                 :            : 
      68                 :            : #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
      69                 :            : static int
      70                 :          2 : my_getpagesize(void)
      71                 :            : {
      72                 :          2 :     return sysconf(_SC_PAGESIZE);
      73                 :            : }
      74                 :            : 
      75                 :            : #define my_getallocationgranularity my_getpagesize
      76                 :            : #else
      77                 :            : #define my_getpagesize getpagesize
      78                 :            : #endif
      79                 :            : 
      80                 :            : #endif /* UNIX */
      81                 :            : 
      82                 :            : #include <string.h>
      83                 :            : 
      84                 :            : #ifdef HAVE_SYS_TYPES_H
      85                 :            : #include <sys/types.h>
      86                 :            : #endif /* HAVE_SYS_TYPES_H */
      87                 :            : 
      88                 :            : /* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
      89                 :            : #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
      90                 :            : #  define MAP_ANONYMOUS MAP_ANON
      91                 :            : #endif
      92                 :            : 
      93                 :            : typedef enum
      94                 :            : {
      95                 :            :     ACCESS_DEFAULT,
      96                 :            :     ACCESS_READ,
      97                 :            :     ACCESS_WRITE,
      98                 :            :     ACCESS_COPY
      99                 :            : } access_mode;
     100                 :            : 
     101                 :            : typedef struct {
     102                 :            :     PyObject_HEAD
     103                 :            :     char *      data;
     104                 :            :     Py_ssize_t  size;
     105                 :            :     Py_ssize_t  pos;    /* relative to offset */
     106                 :            : #ifdef MS_WINDOWS
     107                 :            :     long long offset;
     108                 :            : #else
     109                 :            :     off_t       offset;
     110                 :            : #endif
     111                 :            :     Py_ssize_t  exports;
     112                 :            : 
     113                 :            : #ifdef MS_WINDOWS
     114                 :            :     HANDLE      map_handle;
     115                 :            :     HANDLE      file_handle;
     116                 :            :     char *      tagname;
     117                 :            : #endif
     118                 :            : 
     119                 :            : #ifdef UNIX
     120                 :            :     int fd;
     121                 :            : #endif
     122                 :            : 
     123                 :            :     PyObject *weakreflist;
     124                 :            :     access_mode access;
     125                 :            : } mmap_object;
     126                 :            : 
     127                 :            : static int
     128                 :          0 : mmap_object_traverse(mmap_object *m_obj, visitproc visit, void *arg)
     129                 :            : {
     130   [ #  #  #  # ]:          0 :     Py_VISIT(Py_TYPE(m_obj));
     131                 :          0 :     return 0;
     132                 :            : }
     133                 :            : 
     134                 :            : static void
     135                 :          0 : mmap_object_dealloc(mmap_object *m_obj)
     136                 :            : {
     137                 :          0 :     PyTypeObject *tp = Py_TYPE(m_obj);
     138                 :          0 :     PyObject_GC_UnTrack(m_obj);
     139                 :            : 
     140                 :            : #ifdef MS_WINDOWS
     141                 :            :     Py_BEGIN_ALLOW_THREADS
     142                 :            :     if (m_obj->data != NULL)
     143                 :            :         UnmapViewOfFile (m_obj->data);
     144                 :            :     if (m_obj->map_handle != NULL)
     145                 :            :         CloseHandle (m_obj->map_handle);
     146                 :            :     if (m_obj->file_handle != INVALID_HANDLE_VALUE)
     147                 :            :         CloseHandle (m_obj->file_handle);
     148                 :            :     Py_END_ALLOW_THREADS
     149                 :            :     if (m_obj->tagname)
     150                 :            :         PyMem_Free(m_obj->tagname);
     151                 :            : #endif /* MS_WINDOWS */
     152                 :            : 
     153                 :            : #ifdef UNIX
     154                 :          0 :     Py_BEGIN_ALLOW_THREADS
     155         [ #  # ]:          0 :     if (m_obj->fd >= 0)
     156                 :          0 :         (void) close(m_obj->fd);
     157         [ #  # ]:          0 :     if (m_obj->data!=NULL) {
     158                 :          0 :         munmap(m_obj->data, m_obj->size);
     159                 :            :     }
     160                 :          0 :     Py_END_ALLOW_THREADS
     161                 :            : #endif /* UNIX */
     162                 :            : 
     163         [ #  # ]:          0 :     if (m_obj->weakreflist != NULL)
     164                 :          0 :         PyObject_ClearWeakRefs((PyObject *) m_obj);
     165                 :            : 
     166                 :          0 :     tp->tp_free(m_obj);
     167                 :          0 :     Py_DECREF(tp);
     168                 :          0 : }
     169                 :            : 
     170                 :            : static PyObject *
     171                 :          0 : mmap_close_method(mmap_object *self, PyObject *unused)
     172                 :            : {
     173         [ #  # ]:          0 :     if (self->exports > 0) {
     174                 :          0 :         PyErr_SetString(PyExc_BufferError, "cannot close "\
     175                 :            :                         "exported pointers exist");
     176                 :          0 :         return NULL;
     177                 :            :     }
     178                 :            : #ifdef MS_WINDOWS
     179                 :            :     /* For each resource we maintain, we need to check
     180                 :            :        the value is valid, and if so, free the resource
     181                 :            :        and set the member value to an invalid value so
     182                 :            :        the dealloc does not attempt to resource clearing
     183                 :            :        again.
     184                 :            :        TODO - should we check for errors in the close operations???
     185                 :            :     */
     186                 :            :     HANDLE map_handle = self->map_handle;
     187                 :            :     HANDLE file_handle = self->file_handle;
     188                 :            :     char *data = self->data;
     189                 :            :     self->map_handle = NULL;
     190                 :            :     self->file_handle = INVALID_HANDLE_VALUE;
     191                 :            :     self->data = NULL;
     192                 :            :     Py_BEGIN_ALLOW_THREADS
     193                 :            :     if (data != NULL) {
     194                 :            :         UnmapViewOfFile(data);
     195                 :            :     }
     196                 :            :     if (map_handle != NULL) {
     197                 :            :         CloseHandle(map_handle);
     198                 :            :     }
     199                 :            :     if (file_handle != INVALID_HANDLE_VALUE) {
     200                 :            :         CloseHandle(file_handle);
     201                 :            :     }
     202                 :            :     Py_END_ALLOW_THREADS
     203                 :            : #endif /* MS_WINDOWS */
     204                 :            : 
     205                 :            : #ifdef UNIX
     206                 :          0 :     int fd = self->fd;
     207                 :          0 :     char *data = self->data;
     208                 :          0 :     self->fd = -1;
     209                 :          0 :     self->data = NULL;
     210                 :          0 :     Py_BEGIN_ALLOW_THREADS
     211         [ #  # ]:          0 :     if (0 <= fd)
     212                 :          0 :         (void) close(fd);
     213         [ #  # ]:          0 :     if (data != NULL) {
     214                 :          0 :         munmap(data, self->size);
     215                 :            :     }
     216                 :          0 :     Py_END_ALLOW_THREADS
     217                 :            : #endif
     218                 :            : 
     219                 :          0 :     Py_RETURN_NONE;
     220                 :            : }
     221                 :            : 
     222                 :            : #ifdef MS_WINDOWS
     223                 :            : #define CHECK_VALID(err)                                                \
     224                 :            : do {                                                                    \
     225                 :            :     if (self->map_handle == NULL) {                                     \
     226                 :            :     PyErr_SetString(PyExc_ValueError, "mmap closed or invalid");        \
     227                 :            :     return err;                                                         \
     228                 :            :     }                                                                   \
     229                 :            : } while (0)
     230                 :            : #endif /* MS_WINDOWS */
     231                 :            : 
     232                 :            : #ifdef UNIX
     233                 :            : #define CHECK_VALID(err)                                                \
     234                 :            : do {                                                                    \
     235                 :            :     if (self->data == NULL) {                                           \
     236                 :            :     PyErr_SetString(PyExc_ValueError, "mmap closed or invalid");        \
     237                 :            :     return err;                                                         \
     238                 :            :     }                                                                   \
     239                 :            : } while (0)
     240                 :            : #endif /* UNIX */
     241                 :            : 
     242                 :            : static PyObject *
     243                 :          0 : mmap_read_byte_method(mmap_object *self,
     244                 :            :                       PyObject *unused)
     245                 :            : {
     246         [ #  # ]:          0 :     CHECK_VALID(NULL);
     247         [ #  # ]:          0 :     if (self->pos >= self->size) {
     248                 :          0 :         PyErr_SetString(PyExc_ValueError, "read byte out of range");
     249                 :          0 :         return NULL;
     250                 :            :     }
     251                 :          0 :     return PyLong_FromLong((unsigned char)self->data[self->pos++]);
     252                 :            : }
     253                 :            : 
     254                 :            : static PyObject *
     255                 :          0 : mmap_read_line_method(mmap_object *self,
     256                 :            :                       PyObject *unused)
     257                 :            : {
     258                 :            :     Py_ssize_t remaining;
     259                 :            :     char *start, *eol;
     260                 :            :     PyObject *result;
     261                 :            : 
     262         [ #  # ]:          0 :     CHECK_VALID(NULL);
     263                 :            : 
     264         [ #  # ]:          0 :     remaining = (self->pos < self->size) ? self->size - self->pos : 0;
     265         [ #  # ]:          0 :     if (!remaining)
     266                 :          0 :         return PyBytes_FromString("");
     267                 :          0 :     start = self->data + self->pos;
     268                 :          0 :     eol = memchr(start, '\n', remaining);
     269         [ #  # ]:          0 :     if (!eol)
     270                 :          0 :         eol = self->data + self->size;
     271                 :            :     else
     272                 :          0 :         ++eol; /* advance past newline */
     273                 :          0 :     result = PyBytes_FromStringAndSize(start, (eol - start));
     274                 :          0 :     self->pos += (eol - start);
     275                 :          0 :     return result;
     276                 :            : }
     277                 :            : 
     278                 :            : static PyObject *
     279                 :          0 : mmap_read_method(mmap_object *self,
     280                 :            :                  PyObject *args)
     281                 :            : {
     282                 :          0 :     Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
     283                 :            :     PyObject *result;
     284                 :            : 
     285         [ #  # ]:          0 :     CHECK_VALID(NULL);
     286         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
     287                 :          0 :         return(NULL);
     288                 :            : 
     289                 :            :     /* silently 'adjust' out-of-range requests */
     290         [ #  # ]:          0 :     remaining = (self->pos < self->size) ? self->size - self->pos : 0;
     291   [ #  #  #  # ]:          0 :     if (num_bytes < 0 || num_bytes > remaining)
     292                 :          0 :         num_bytes = remaining;
     293                 :          0 :     result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
     294                 :          0 :     self->pos += num_bytes;
     295                 :          0 :     return result;
     296                 :            : }
     297                 :            : 
     298                 :            : static PyObject *
     299                 :          0 : mmap_gfind(mmap_object *self,
     300                 :            :            PyObject *args,
     301                 :            :            int reverse)
     302                 :            : {
     303                 :          0 :     Py_ssize_t start = self->pos;
     304                 :          0 :     Py_ssize_t end = self->size;
     305                 :            :     Py_buffer view;
     306                 :            : 
     307         [ #  # ]:          0 :     CHECK_VALID(NULL);
     308   [ #  #  #  # ]:          0 :     if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
     309                 :            :                           &view, &start, &end)) {
     310                 :          0 :         return NULL;
     311                 :            :     }
     312                 :            :     else {
     313         [ #  # ]:          0 :         if (start < 0)
     314                 :          0 :             start += self->size;
     315         [ #  # ]:          0 :         if (start < 0)
     316                 :          0 :             start = 0;
     317         [ #  # ]:          0 :         else if (start > self->size)
     318                 :          0 :             start = self->size;
     319                 :            : 
     320         [ #  # ]:          0 :         if (end < 0)
     321                 :          0 :             end += self->size;
     322         [ #  # ]:          0 :         if (end < 0)
     323                 :          0 :             end = 0;
     324         [ #  # ]:          0 :         else if (end > self->size)
     325                 :          0 :             end = self->size;
     326                 :            : 
     327                 :            :         Py_ssize_t res;
     328         [ #  # ]:          0 :         if (reverse) {
     329                 :          0 :             res = _PyBytes_ReverseFind(
     330                 :          0 :                 self->data + start, end - start,
     331                 :          0 :                 view.buf, view.len, start);
     332                 :            :         }
     333                 :            :         else {
     334                 :          0 :             res = _PyBytes_Find(
     335                 :          0 :                 self->data + start, end - start,
     336                 :          0 :                 view.buf, view.len, start);
     337                 :            :         }
     338                 :          0 :         PyBuffer_Release(&view);
     339                 :          0 :         return PyLong_FromSsize_t(res);
     340                 :            :     }
     341                 :            : }
     342                 :            : 
     343                 :            : static PyObject *
     344                 :          0 : mmap_find_method(mmap_object *self,
     345                 :            :                  PyObject *args)
     346                 :            : {
     347                 :          0 :     return mmap_gfind(self, args, 0);
     348                 :            : }
     349                 :            : 
     350                 :            : static PyObject *
     351                 :          0 : mmap_rfind_method(mmap_object *self,
     352                 :            :                  PyObject *args)
     353                 :            : {
     354                 :          0 :     return mmap_gfind(self, args, 1);
     355                 :            : }
     356                 :            : 
     357                 :            : static int
     358                 :          0 : is_writable(mmap_object *self)
     359                 :            : {
     360         [ #  # ]:          0 :     if (self->access != ACCESS_READ)
     361                 :          0 :         return 1;
     362                 :          0 :     PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
     363                 :          0 :     return 0;
     364                 :            : }
     365                 :            : 
     366                 :            : static int
     367                 :          0 : is_resizeable(mmap_object *self)
     368                 :            : {
     369         [ #  # ]:          0 :     if (self->exports > 0) {
     370                 :          0 :         PyErr_SetString(PyExc_BufferError,
     371                 :            :             "mmap can't resize with extant buffers exported.");
     372                 :          0 :         return 0;
     373                 :            :     }
     374   [ #  #  #  # ]:          0 :     if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
     375                 :          0 :         return 1;
     376                 :          0 :     PyErr_Format(PyExc_TypeError,
     377                 :            :         "mmap can't resize a readonly or copy-on-write memory map.");
     378                 :          0 :     return 0;
     379                 :            : 
     380                 :            : }
     381                 :            : 
     382                 :            : 
     383                 :            : static PyObject *
     384                 :          0 : mmap_write_method(mmap_object *self,
     385                 :            :                   PyObject *args)
     386                 :            : {
     387                 :            :     Py_buffer data;
     388                 :            : 
     389         [ #  # ]:          0 :     CHECK_VALID(NULL);
     390         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "y*:write", &data))
     391                 :          0 :         return(NULL);
     392                 :            : 
     393         [ #  # ]:          0 :     if (!is_writable(self)) {
     394                 :          0 :         PyBuffer_Release(&data);
     395                 :          0 :         return NULL;
     396                 :            :     }
     397                 :            : 
     398   [ #  #  #  # ]:          0 :     if (self->pos > self->size || self->size - self->pos < data.len) {
     399                 :          0 :         PyBuffer_Release(&data);
     400                 :          0 :         PyErr_SetString(PyExc_ValueError, "data out of range");
     401                 :          0 :         return NULL;
     402                 :            :     }
     403                 :            : 
     404                 :          0 :     memcpy(&self->data[self->pos], data.buf, data.len);
     405                 :          0 :     self->pos += data.len;
     406                 :          0 :     PyBuffer_Release(&data);
     407                 :          0 :     return PyLong_FromSsize_t(data.len);
     408                 :            : }
     409                 :            : 
     410                 :            : static PyObject *
     411                 :          0 : mmap_write_byte_method(mmap_object *self,
     412                 :            :                        PyObject *args)
     413                 :            : {
     414                 :            :     char value;
     415                 :            : 
     416         [ #  # ]:          0 :     CHECK_VALID(NULL);
     417         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "b:write_byte", &value))
     418                 :          0 :         return(NULL);
     419                 :            : 
     420         [ #  # ]:          0 :     if (!is_writable(self))
     421                 :          0 :         return NULL;
     422                 :            : 
     423         [ #  # ]:          0 :     if (self->pos < self->size) {
     424                 :          0 :         self->data[self->pos++] = value;
     425                 :          0 :         Py_RETURN_NONE;
     426                 :            :     }
     427                 :            :     else {
     428                 :          0 :         PyErr_SetString(PyExc_ValueError, "write byte out of range");
     429                 :          0 :         return NULL;
     430                 :            :     }
     431                 :            : }
     432                 :            : 
     433                 :            : static PyObject *
     434                 :          0 : mmap_size_method(mmap_object *self,
     435                 :            :                  PyObject *unused)
     436                 :            : {
     437         [ #  # ]:          0 :     CHECK_VALID(NULL);
     438                 :            : 
     439                 :            : #ifdef MS_WINDOWS
     440                 :            :     if (self->file_handle != INVALID_HANDLE_VALUE) {
     441                 :            :         DWORD low,high;
     442                 :            :         long long size;
     443                 :            :         low = GetFileSize(self->file_handle, &high);
     444                 :            :         if (low == INVALID_FILE_SIZE) {
     445                 :            :             /* It might be that the function appears to have failed,
     446                 :            :                when indeed its size equals INVALID_FILE_SIZE */
     447                 :            :             DWORD error = GetLastError();
     448                 :            :             if (error != NO_ERROR)
     449                 :            :                 return PyErr_SetFromWindowsErr(error);
     450                 :            :         }
     451                 :            :         if (!high && low < LONG_MAX)
     452                 :            :             return PyLong_FromLong((long)low);
     453                 :            :         size = (((long long)high)<<32) + low;
     454                 :            :         return PyLong_FromLongLong(size);
     455                 :            :     } else {
     456                 :            :         return PyLong_FromSsize_t(self->size);
     457                 :            :     }
     458                 :            : #endif /* MS_WINDOWS */
     459                 :            : 
     460                 :            : #ifdef UNIX
     461                 :            :     {
     462                 :            :         struct _Py_stat_struct status;
     463         [ #  # ]:          0 :         if (_Py_fstat(self->fd, &status) == -1)
     464                 :          0 :             return NULL;
     465                 :            : #ifdef HAVE_LARGEFILE_SUPPORT
     466                 :            :         return PyLong_FromLongLong(status.st_size);
     467                 :            : #else
     468                 :          0 :         return PyLong_FromLong(status.st_size);
     469                 :            : #endif
     470                 :            :     }
     471                 :            : #endif /* UNIX */
     472                 :            : }
     473                 :            : 
     474                 :            : /* This assumes that you want the entire file mapped,
     475                 :            :  / and when recreating the map will make the new file
     476                 :            :  / have the new size
     477                 :            :  /
     478                 :            :  / Is this really necessary?  This could easily be done
     479                 :            :  / from python by just closing and re-opening with the
     480                 :            :  / new size?
     481                 :            :  */
     482                 :            : 
     483                 :            : static PyObject *
     484                 :          0 : mmap_resize_method(mmap_object *self,
     485                 :            :                    PyObject *args)
     486                 :            : {
     487                 :            :     Py_ssize_t new_size;
     488         [ #  # ]:          0 :     CHECK_VALID(NULL);
     489   [ #  #  #  # ]:          0 :     if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
     490                 :          0 :         !is_resizeable(self)) {
     491                 :          0 :         return NULL;
     492                 :            :     }
     493   [ #  #  #  # ]:          0 :     if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
     494                 :          0 :         PyErr_SetString(PyExc_ValueError, "new size out of range");
     495                 :          0 :         return NULL;
     496                 :            :     }
     497                 :            : 
     498                 :            :     {
     499                 :            : #ifdef MS_WINDOWS
     500                 :            :         DWORD error = 0, file_resize_error = 0;
     501                 :            :         char* old_data = self->data;
     502                 :            :         LARGE_INTEGER offset, max_size;
     503                 :            :         offset.QuadPart = self->offset;
     504                 :            :         max_size.QuadPart = self->offset + new_size;
     505                 :            :         /* close the file mapping */
     506                 :            :         CloseHandle(self->map_handle);
     507                 :            :         /* if the file mapping still exists, it cannot be resized. */
     508                 :            :         if (self->tagname) {
     509                 :            :             self->map_handle = OpenFileMappingA(FILE_MAP_WRITE, FALSE,
     510                 :            :                                     self->tagname);
     511                 :            :             if (self->map_handle) {
     512                 :            :                 PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE);
     513                 :            :                 return NULL;
     514                 :            :             }
     515                 :            :         } else {
     516                 :            :             self->map_handle = NULL;
     517                 :            :         }
     518                 :            : 
     519                 :            :         /* if it's not the paging file, unmap the view and resize the file */
     520                 :            :         if (self->file_handle != INVALID_HANDLE_VALUE) {
     521                 :            :             if (!UnmapViewOfFile(self->data)) {
     522                 :            :                 return PyErr_SetFromWindowsErr(GetLastError());
     523                 :            :             };
     524                 :            :             self->data = NULL;
     525                 :            :             /* resize the file */
     526                 :            :             if (!SetFilePointerEx(self->file_handle, max_size, NULL,
     527                 :            :                 FILE_BEGIN) ||
     528                 :            :                 !SetEndOfFile(self->file_handle)) {
     529                 :            :                 /* resizing failed. try to remap the file */
     530                 :            :                 file_resize_error = GetLastError();
     531                 :            :                 max_size.QuadPart = self->size;
     532                 :            :                 new_size = self->size;
     533                 :            :             }
     534                 :            :         }
     535                 :            : 
     536                 :            :         /* create a new file mapping and map a new view */
     537                 :            :         /* FIXME: call CreateFileMappingW with wchar_t tagname */
     538                 :            :         self->map_handle = CreateFileMappingA(
     539                 :            :             self->file_handle,
     540                 :            :             NULL,
     541                 :            :             PAGE_READWRITE,
     542                 :            :             max_size.HighPart,
     543                 :            :             max_size.LowPart,
     544                 :            :             self->tagname);
     545                 :            : 
     546                 :            :         error = GetLastError();
     547                 :            :         /* ERROR_ALREADY_EXISTS implies that between our closing the handle above and
     548                 :            :         calling CreateFileMapping here, someone's created a different mapping with
     549                 :            :         the same name. There's nothing we can usefully do so we invalidate our
     550                 :            :         mapping and error out.
     551                 :            :         */
     552                 :            :         if (error == ERROR_ALREADY_EXISTS) {
     553                 :            :             CloseHandle(self->map_handle);
     554                 :            :             self->map_handle = NULL;
     555                 :            :         }
     556                 :            :         else if (self->map_handle != NULL) {
     557                 :            :             self->data = MapViewOfFile(self->map_handle,
     558                 :            :                 FILE_MAP_WRITE,
     559                 :            :                 offset.HighPart,
     560                 :            :                 offset.LowPart,
     561                 :            :                 new_size);
     562                 :            :             if (self->data != NULL) {
     563                 :            :                 /* copy the old view if using the paging file */
     564                 :            :                 if (self->file_handle == INVALID_HANDLE_VALUE) {
     565                 :            :                     memcpy(self->data, old_data,
     566                 :            :                            self->size < new_size ? self->size : new_size);
     567                 :            :                     if (!UnmapViewOfFile(old_data)) {
     568                 :            :                         error = GetLastError();
     569                 :            :                     }
     570                 :            :                 }
     571                 :            :                 self->size = new_size;
     572                 :            :             }
     573                 :            :             else {
     574                 :            :                 error = GetLastError();
     575                 :            :                 CloseHandle(self->map_handle);
     576                 :            :                 self->map_handle = NULL;
     577                 :            :             }
     578                 :            :         }
     579                 :            : 
     580                 :            :         if (error) {
     581                 :            :             return PyErr_SetFromWindowsErr(error);
     582                 :            :             return NULL;
     583                 :            :         }
     584                 :            :         /* It's possible for a resize to fail, typically because another mapping
     585                 :            :         is still held against the same underlying file. Even if nothing has
     586                 :            :         failed -- ie we're still returning a valid file mapping -- raise the
     587                 :            :         error as an exception as the resize won't have happened
     588                 :            :         */
     589                 :            :         if (file_resize_error) {
     590                 :            :             PyErr_SetFromWindowsErr(file_resize_error);
     591                 :            :             return NULL;
     592                 :            :         }
     593                 :            :         Py_RETURN_NONE;
     594                 :            : #endif /* MS_WINDOWS */
     595                 :            : 
     596                 :            : #ifdef UNIX
     597                 :            : #ifndef HAVE_MREMAP
     598                 :            :         PyErr_SetString(PyExc_SystemError,
     599                 :            :                         "mmap: resizing not available--no mremap()");
     600                 :            :         return NULL;
     601                 :            : #else
     602                 :            :         void *newmap;
     603                 :            : 
     604   [ #  #  #  # ]:          0 :         if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
     605                 :          0 :             PyErr_SetFromErrno(PyExc_OSError);
     606                 :          0 :             return NULL;
     607                 :            :         }
     608                 :            : 
     609                 :            : #ifdef MREMAP_MAYMOVE
     610                 :          0 :         newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
     611                 :            : #else
     612                 :            : #if defined(__NetBSD__)
     613                 :            :         newmap = mremap(self->data, self->size, self->data, new_size, 0);
     614                 :            : #else
     615                 :            :         newmap = mremap(self->data, self->size, new_size, 0);
     616                 :            : #endif /* __NetBSD__ */
     617                 :            : #endif
     618         [ #  # ]:          0 :         if (newmap == (void *)-1)
     619                 :            :         {
     620                 :          0 :             PyErr_SetFromErrno(PyExc_OSError);
     621                 :          0 :             return NULL;
     622                 :            :         }
     623                 :          0 :         self->data = newmap;
     624                 :          0 :         self->size = new_size;
     625                 :          0 :         Py_RETURN_NONE;
     626                 :            : #endif /* HAVE_MREMAP */
     627                 :            : #endif /* UNIX */
     628                 :            :     }
     629                 :            : }
     630                 :            : 
     631                 :            : static PyObject *
     632                 :          0 : mmap_tell_method(mmap_object *self, PyObject *unused)
     633                 :            : {
     634         [ #  # ]:          0 :     CHECK_VALID(NULL);
     635                 :          0 :     return PyLong_FromSize_t(self->pos);
     636                 :            : }
     637                 :            : 
     638                 :            : static PyObject *
     639                 :          0 : mmap_flush_method(mmap_object *self, PyObject *args)
     640                 :            : {
     641                 :          0 :     Py_ssize_t offset = 0;
     642                 :          0 :     Py_ssize_t size = self->size;
     643         [ #  # ]:          0 :     CHECK_VALID(NULL);
     644         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
     645                 :          0 :         return NULL;
     646   [ #  #  #  #  :          0 :     if (size < 0 || offset < 0 || self->size - offset < size) {
                   #  # ]
     647                 :          0 :         PyErr_SetString(PyExc_ValueError, "flush values out of range");
     648                 :          0 :         return NULL;
     649                 :            :     }
     650                 :            : 
     651   [ #  #  #  # ]:          0 :     if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
     652                 :          0 :         Py_RETURN_NONE;
     653                 :            : 
     654                 :            : #if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM)
     655                 :            :     if (!FlushViewOfFile(self->data+offset, size)) {
     656                 :            :         PyErr_SetFromWindowsErr(GetLastError());
     657                 :            :         return NULL;
     658                 :            :     }
     659                 :            :     Py_RETURN_NONE;
     660                 :            : #elif defined(UNIX)
     661                 :            :     /* XXX flags for msync? */
     662         [ #  # ]:          0 :     if (-1 == msync(self->data + offset, size, MS_SYNC)) {
     663                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
     664                 :          0 :         return NULL;
     665                 :            :     }
     666                 :          0 :     Py_RETURN_NONE;
     667                 :            : #else
     668                 :            :     PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
     669                 :            :     return NULL;
     670                 :            : #endif
     671                 :            : }
     672                 :            : 
     673                 :            : static PyObject *
     674                 :          0 : mmap_seek_method(mmap_object *self, PyObject *args)
     675                 :            : {
     676                 :            :     Py_ssize_t dist;
     677                 :          0 :     int how=0;
     678         [ #  # ]:          0 :     CHECK_VALID(NULL);
     679         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
     680                 :          0 :         return NULL;
     681                 :            :     else {
     682                 :            :         Py_ssize_t where;
     683   [ #  #  #  # ]:          0 :         switch (how) {
     684                 :          0 :         case 0: /* relative to start */
     685                 :          0 :             where = dist;
     686                 :          0 :             break;
     687                 :          0 :         case 1: /* relative to current position */
     688         [ #  # ]:          0 :             if (PY_SSIZE_T_MAX - self->pos < dist)
     689                 :          0 :                 goto onoutofrange;
     690                 :          0 :             where = self->pos + dist;
     691                 :          0 :             break;
     692                 :          0 :         case 2: /* relative to end */
     693         [ #  # ]:          0 :             if (PY_SSIZE_T_MAX - self->size < dist)
     694                 :          0 :                 goto onoutofrange;
     695                 :          0 :             where = self->size + dist;
     696                 :          0 :             break;
     697                 :          0 :         default:
     698                 :          0 :             PyErr_SetString(PyExc_ValueError, "unknown seek type");
     699                 :          0 :             return NULL;
     700                 :            :         }
     701   [ #  #  #  # ]:          0 :         if (where > self->size || where < 0)
     702                 :          0 :             goto onoutofrange;
     703                 :          0 :         self->pos = where;
     704                 :          0 :         Py_RETURN_NONE;
     705                 :            :     }
     706                 :            : 
     707                 :          0 :   onoutofrange:
     708                 :          0 :     PyErr_SetString(PyExc_ValueError, "seek out of range");
     709                 :          0 :     return NULL;
     710                 :            : }
     711                 :            : 
     712                 :            : static PyObject *
     713                 :          0 : mmap_move_method(mmap_object *self, PyObject *args)
     714                 :            : {
     715                 :            :     Py_ssize_t dest, src, cnt;
     716         [ #  # ]:          0 :     CHECK_VALID(NULL);
     717   [ #  #  #  # ]:          0 :     if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
     718                 :          0 :         !is_writable(self)) {
     719                 :          0 :         return NULL;
     720                 :            :     } else {
     721                 :            :         /* bounds check the values */
     722   [ #  #  #  #  :          0 :         if (dest < 0 || src < 0 || cnt < 0)
                   #  # ]
     723                 :          0 :             goto bounds;
     724   [ #  #  #  # ]:          0 :         if (self->size - dest < cnt || self->size - src < cnt)
     725                 :          0 :             goto bounds;
     726                 :            : 
     727                 :          0 :         memmove(&self->data[dest], &self->data[src], cnt);
     728                 :            : 
     729                 :          0 :         Py_RETURN_NONE;
     730                 :            : 
     731                 :          0 :       bounds:
     732                 :          0 :         PyErr_SetString(PyExc_ValueError,
     733                 :            :                         "source, destination, or count out of range");
     734                 :          0 :         return NULL;
     735                 :            :     }
     736                 :            : }
     737                 :            : 
     738                 :            : static PyObject *
     739                 :          0 : mmap_closed_get(mmap_object *self, void *Py_UNUSED(ignored))
     740                 :            : {
     741                 :            : #ifdef MS_WINDOWS
     742                 :            :     return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
     743                 :            : #elif defined(UNIX)
     744                 :          0 :     return PyBool_FromLong(self->data == NULL ? 1 : 0);
     745                 :            : #endif
     746                 :            : }
     747                 :            : 
     748                 :            : static PyObject *
     749                 :          0 : mmap__enter__method(mmap_object *self, PyObject *args)
     750                 :            : {
     751         [ #  # ]:          0 :     CHECK_VALID(NULL);
     752                 :            : 
     753                 :          0 :     return Py_NewRef(self);
     754                 :            : }
     755                 :            : 
     756                 :            : static PyObject *
     757                 :          0 : mmap__exit__method(PyObject *self, PyObject *args)
     758                 :            : {
     759                 :          0 :     return mmap_close_method((mmap_object *)self, NULL);
     760                 :            : }
     761                 :            : 
     762                 :            : static PyObject *
     763                 :          0 : mmap__repr__method(PyObject *self)
     764                 :            : {
     765                 :          0 :     mmap_object *mobj = (mmap_object *)self;
     766                 :            : 
     767                 :            : #ifdef MS_WINDOWS
     768                 :            : #define _Py_FORMAT_OFFSET "lld"
     769                 :            :     if (mobj->map_handle == NULL)
     770                 :            : #elif defined(UNIX)
     771                 :            : # ifdef HAVE_LARGEFILE_SUPPORT
     772                 :            : # define _Py_FORMAT_OFFSET "lld"
     773                 :            : # else
     774                 :            : # define _Py_FORMAT_OFFSET "ld"
     775                 :            : # endif
     776         [ #  # ]:          0 :     if (mobj->data == NULL)
     777                 :            : #endif
     778                 :            :     {
     779                 :          0 :         return PyUnicode_FromFormat("<%s closed=True>", Py_TYPE(self)->tp_name);
     780                 :            :     } else {
     781                 :            :         const char *access_str;
     782                 :            : 
     783   [ #  #  #  #  :          0 :         switch (mobj->access) {
                      # ]
     784                 :          0 :             case ACCESS_DEFAULT:
     785                 :          0 :                 access_str = "ACCESS_DEFAULT";
     786                 :          0 :                 break;
     787                 :          0 :             case ACCESS_READ:
     788                 :          0 :                 access_str = "ACCESS_READ";
     789                 :          0 :                 break;
     790                 :          0 :             case ACCESS_WRITE:
     791                 :          0 :                 access_str = "ACCESS_WRITE";
     792                 :          0 :                 break;
     793                 :          0 :             case ACCESS_COPY:
     794                 :          0 :                 access_str = "ACCESS_COPY";
     795                 :          0 :                 break;
     796                 :          0 :             default:
     797                 :          0 :                 Py_UNREACHABLE();
     798                 :            :         }
     799                 :            : 
     800                 :          0 :         return PyUnicode_FromFormat("<%s closed=False, access=%s, length=%zd, "
     801                 :            :                                     "pos=%zd, offset=%" _Py_FORMAT_OFFSET ">",
     802                 :          0 :                                     Py_TYPE(self)->tp_name, access_str,
     803                 :            :                                     mobj->size, mobj->pos, mobj->offset);
     804                 :            :     }
     805                 :            : }
     806                 :            : 
     807                 :            : #ifdef MS_WINDOWS
     808                 :            : static PyObject *
     809                 :            : mmap__sizeof__method(mmap_object *self, void *unused)
     810                 :            : {
     811                 :            :     size_t res = _PyObject_SIZE(Py_TYPE(self));
     812                 :            :     if (self->tagname) {
     813                 :            :         res += strlen(self->tagname) + 1;
     814                 :            :     }
     815                 :            :     return PyLong_FromSize_t(res);
     816                 :            : }
     817                 :            : #endif
     818                 :            : 
     819                 :            : #ifdef HAVE_MADVISE
     820                 :            : static PyObject *
     821                 :          0 : mmap_madvise_method(mmap_object *self, PyObject *args)
     822                 :            : {
     823                 :            :     int option;
     824                 :          0 :     Py_ssize_t start = 0, length;
     825                 :            : 
     826         [ #  # ]:          0 :     CHECK_VALID(NULL);
     827                 :          0 :     length = self->size;
     828                 :            : 
     829         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) {
     830                 :          0 :         return NULL;
     831                 :            :     }
     832                 :            : 
     833   [ #  #  #  # ]:          0 :     if (start < 0 || start >= self->size) {
     834                 :          0 :         PyErr_SetString(PyExc_ValueError, "madvise start out of bounds");
     835                 :          0 :         return NULL;
     836                 :            :     }
     837         [ #  # ]:          0 :     if (length < 0) {
     838                 :          0 :         PyErr_SetString(PyExc_ValueError, "madvise length invalid");
     839                 :          0 :         return NULL;
     840                 :            :     }
     841         [ #  # ]:          0 :     if (PY_SSIZE_T_MAX - start < length) {
     842                 :          0 :         PyErr_SetString(PyExc_OverflowError, "madvise length too large");
     843                 :          0 :         return NULL;
     844                 :            :     }
     845                 :            : 
     846         [ #  # ]:          0 :     if (start + length > self->size) {
     847                 :          0 :         length = self->size - start;
     848                 :            :     }
     849                 :            : 
     850         [ #  # ]:          0 :     if (madvise(self->data + start, length, option) != 0) {
     851                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
     852                 :          0 :         return NULL;
     853                 :            :     }
     854                 :            : 
     855                 :          0 :     Py_RETURN_NONE;
     856                 :            : }
     857                 :            : #endif // HAVE_MADVISE
     858                 :            : 
     859                 :            : static struct PyMemberDef mmap_object_members[] = {
     860                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(mmap_object, weakreflist), READONLY},
     861                 :            :     {NULL},
     862                 :            : };
     863                 :            : 
     864                 :            : static struct PyMethodDef mmap_object_methods[] = {
     865                 :            :     {"close",           (PyCFunction) mmap_close_method,        METH_NOARGS},
     866                 :            :     {"find",            (PyCFunction) mmap_find_method,         METH_VARARGS},
     867                 :            :     {"rfind",           (PyCFunction) mmap_rfind_method,        METH_VARARGS},
     868                 :            :     {"flush",           (PyCFunction) mmap_flush_method,        METH_VARARGS},
     869                 :            : #ifdef HAVE_MADVISE
     870                 :            :     {"madvise",         (PyCFunction) mmap_madvise_method,      METH_VARARGS},
     871                 :            : #endif
     872                 :            :     {"move",            (PyCFunction) mmap_move_method,         METH_VARARGS},
     873                 :            :     {"read",            (PyCFunction) mmap_read_method,         METH_VARARGS},
     874                 :            :     {"read_byte",       (PyCFunction) mmap_read_byte_method,    METH_NOARGS},
     875                 :            :     {"readline",        (PyCFunction) mmap_read_line_method,    METH_NOARGS},
     876                 :            :     {"resize",          (PyCFunction) mmap_resize_method,       METH_VARARGS},
     877                 :            :     {"seek",            (PyCFunction) mmap_seek_method,         METH_VARARGS},
     878                 :            :     {"size",            (PyCFunction) mmap_size_method,         METH_NOARGS},
     879                 :            :     {"tell",            (PyCFunction) mmap_tell_method,         METH_NOARGS},
     880                 :            :     {"write",           (PyCFunction) mmap_write_method,        METH_VARARGS},
     881                 :            :     {"write_byte",      (PyCFunction) mmap_write_byte_method,   METH_VARARGS},
     882                 :            :     {"__enter__",       (PyCFunction) mmap__enter__method,      METH_NOARGS},
     883                 :            :     {"__exit__",        (PyCFunction) mmap__exit__method,       METH_VARARGS},
     884                 :            : #ifdef MS_WINDOWS
     885                 :            :     {"__sizeof__",      (PyCFunction) mmap__sizeof__method,     METH_NOARGS},
     886                 :            : #endif
     887                 :            :     {NULL,         NULL}       /* sentinel */
     888                 :            : };
     889                 :            : 
     890                 :            : static PyGetSetDef mmap_object_getset[] = {
     891                 :            :     {"closed", (getter) mmap_closed_get, NULL, NULL},
     892                 :            :     {NULL}
     893                 :            : };
     894                 :            : 
     895                 :            : 
     896                 :            : /* Functions for treating an mmap'ed file as a buffer */
     897                 :            : 
     898                 :            : static int
     899                 :          0 : mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
     900                 :            : {
     901         [ #  # ]:          0 :     CHECK_VALID(-1);
     902         [ #  # ]:          0 :     if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
     903                 :          0 :                           (self->access == ACCESS_READ), flags) < 0)
     904                 :          0 :         return -1;
     905                 :          0 :     self->exports++;
     906                 :          0 :     return 0;
     907                 :            : }
     908                 :            : 
     909                 :            : static void
     910                 :          0 : mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
     911                 :            : {
     912                 :          0 :     self->exports--;
     913                 :          0 : }
     914                 :            : 
     915                 :            : static Py_ssize_t
     916                 :          0 : mmap_length(mmap_object *self)
     917                 :            : {
     918         [ #  # ]:          0 :     CHECK_VALID(-1);
     919                 :          0 :     return self->size;
     920                 :            : }
     921                 :            : 
     922                 :            : static PyObject *
     923                 :          0 : mmap_item(mmap_object *self, Py_ssize_t i)
     924                 :            : {
     925         [ #  # ]:          0 :     CHECK_VALID(NULL);
     926   [ #  #  #  # ]:          0 :     if (i < 0 || i >= self->size) {
     927                 :          0 :         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
     928                 :          0 :         return NULL;
     929                 :            :     }
     930                 :          0 :     return PyBytes_FromStringAndSize(self->data + i, 1);
     931                 :            : }
     932                 :            : 
     933                 :            : static PyObject *
     934                 :          0 : mmap_subscript(mmap_object *self, PyObject *item)
     935                 :            : {
     936         [ #  # ]:          0 :     CHECK_VALID(NULL);
     937         [ #  # ]:          0 :     if (PyIndex_Check(item)) {
     938                 :          0 :         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
     939   [ #  #  #  # ]:          0 :         if (i == -1 && PyErr_Occurred())
     940                 :          0 :             return NULL;
     941         [ #  # ]:          0 :         if (i < 0)
     942                 :          0 :             i += self->size;
     943   [ #  #  #  # ]:          0 :         if (i < 0 || i >= self->size) {
     944                 :          0 :             PyErr_SetString(PyExc_IndexError,
     945                 :            :                 "mmap index out of range");
     946                 :          0 :             return NULL;
     947                 :            :         }
     948                 :          0 :         return PyLong_FromLong(Py_CHARMASK(self->data[i]));
     949                 :            :     }
     950         [ #  # ]:          0 :     else if (PySlice_Check(item)) {
     951                 :            :         Py_ssize_t start, stop, step, slicelen;
     952                 :            : 
     953         [ #  # ]:          0 :         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
     954                 :          0 :             return NULL;
     955                 :            :         }
     956                 :          0 :         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
     957                 :            : 
     958         [ #  # ]:          0 :         if (slicelen <= 0)
     959                 :          0 :             return PyBytes_FromStringAndSize("", 0);
     960         [ #  # ]:          0 :         else if (step == 1)
     961                 :          0 :             return PyBytes_FromStringAndSize(self->data + start,
     962                 :            :                                               slicelen);
     963                 :            :         else {
     964                 :          0 :             char *result_buf = (char *)PyMem_Malloc(slicelen);
     965                 :            :             size_t cur;
     966                 :            :             Py_ssize_t i;
     967                 :            :             PyObject *result;
     968                 :            : 
     969         [ #  # ]:          0 :             if (result_buf == NULL)
     970                 :          0 :                 return PyErr_NoMemory();
     971         [ #  # ]:          0 :             for (cur = start, i = 0; i < slicelen;
     972                 :          0 :                  cur += step, i++) {
     973                 :          0 :                 result_buf[i] = self->data[cur];
     974                 :            :             }
     975                 :          0 :             result = PyBytes_FromStringAndSize(result_buf,
     976                 :            :                                                 slicelen);
     977                 :          0 :             PyMem_Free(result_buf);
     978                 :          0 :             return result;
     979                 :            :         }
     980                 :            :     }
     981                 :            :     else {
     982                 :          0 :         PyErr_SetString(PyExc_TypeError,
     983                 :            :                         "mmap indices must be integers");
     984                 :          0 :         return NULL;
     985                 :            :     }
     986                 :            : }
     987                 :            : 
     988                 :            : static int
     989                 :          0 : mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
     990                 :            : {
     991                 :            :     const char *buf;
     992                 :            : 
     993         [ #  # ]:          0 :     CHECK_VALID(-1);
     994   [ #  #  #  # ]:          0 :     if (i < 0 || i >= self->size) {
     995                 :          0 :         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
     996                 :          0 :         return -1;
     997                 :            :     }
     998         [ #  # ]:          0 :     if (v == NULL) {
     999                 :          0 :         PyErr_SetString(PyExc_TypeError,
    1000                 :            :                         "mmap object doesn't support item deletion");
    1001                 :          0 :         return -1;
    1002                 :            :     }
    1003   [ #  #  #  # ]:          0 :     if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
    1004                 :          0 :         PyErr_SetString(PyExc_IndexError,
    1005                 :            :                         "mmap assignment must be length-1 bytes()");
    1006                 :          0 :         return -1;
    1007                 :            :     }
    1008         [ #  # ]:          0 :     if (!is_writable(self))
    1009                 :          0 :         return -1;
    1010                 :          0 :     buf = PyBytes_AsString(v);
    1011                 :          0 :     self->data[i] = buf[0];
    1012                 :          0 :     return 0;
    1013                 :            : }
    1014                 :            : 
    1015                 :            : static int
    1016                 :          0 : mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
    1017                 :            : {
    1018         [ #  # ]:          0 :     CHECK_VALID(-1);
    1019                 :            : 
    1020         [ #  # ]:          0 :     if (!is_writable(self))
    1021                 :          0 :         return -1;
    1022                 :            : 
    1023         [ #  # ]:          0 :     if (PyIndex_Check(item)) {
    1024                 :          0 :         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
    1025                 :            :         Py_ssize_t v;
    1026                 :            : 
    1027   [ #  #  #  # ]:          0 :         if (i == -1 && PyErr_Occurred())
    1028                 :          0 :             return -1;
    1029         [ #  # ]:          0 :         if (i < 0)
    1030                 :          0 :             i += self->size;
    1031   [ #  #  #  # ]:          0 :         if (i < 0 || i >= self->size) {
    1032                 :          0 :             PyErr_SetString(PyExc_IndexError,
    1033                 :            :                             "mmap index out of range");
    1034                 :          0 :             return -1;
    1035                 :            :         }
    1036         [ #  # ]:          0 :         if (value == NULL) {
    1037                 :          0 :             PyErr_SetString(PyExc_TypeError,
    1038                 :            :                             "mmap doesn't support item deletion");
    1039                 :          0 :             return -1;
    1040                 :            :         }
    1041         [ #  # ]:          0 :         if (!PyIndex_Check(value)) {
    1042                 :          0 :             PyErr_SetString(PyExc_TypeError,
    1043                 :            :                             "mmap item value must be an int");
    1044                 :          0 :             return -1;
    1045                 :            :         }
    1046                 :          0 :         v = PyNumber_AsSsize_t(value, PyExc_TypeError);
    1047   [ #  #  #  # ]:          0 :         if (v == -1 && PyErr_Occurred())
    1048                 :          0 :             return -1;
    1049   [ #  #  #  # ]:          0 :         if (v < 0 || v > 255) {
    1050                 :          0 :             PyErr_SetString(PyExc_ValueError,
    1051                 :            :                             "mmap item value must be "
    1052                 :            :                             "in range(0, 256)");
    1053                 :          0 :             return -1;
    1054                 :            :         }
    1055                 :          0 :         self->data[i] = (char) v;
    1056                 :          0 :         return 0;
    1057                 :            :     }
    1058         [ #  # ]:          0 :     else if (PySlice_Check(item)) {
    1059                 :            :         Py_ssize_t start, stop, step, slicelen;
    1060                 :            :         Py_buffer vbuf;
    1061                 :            : 
    1062         [ #  # ]:          0 :         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
    1063                 :          0 :             return -1;
    1064                 :            :         }
    1065                 :          0 :         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
    1066         [ #  # ]:          0 :         if (value == NULL) {
    1067                 :          0 :             PyErr_SetString(PyExc_TypeError,
    1068                 :            :                 "mmap object doesn't support slice deletion");
    1069                 :          0 :             return -1;
    1070                 :            :         }
    1071         [ #  # ]:          0 :         if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
    1072                 :          0 :             return -1;
    1073         [ #  # ]:          0 :         if (vbuf.len != slicelen) {
    1074                 :          0 :             PyErr_SetString(PyExc_IndexError,
    1075                 :            :                 "mmap slice assignment is wrong size");
    1076                 :          0 :             PyBuffer_Release(&vbuf);
    1077                 :          0 :             return -1;
    1078                 :            :         }
    1079                 :            : 
    1080         [ #  # ]:          0 :         if (slicelen == 0) {
    1081                 :            :         }
    1082         [ #  # ]:          0 :         else if (step == 1) {
    1083                 :          0 :             memcpy(self->data + start, vbuf.buf, slicelen);
    1084                 :            :         }
    1085                 :            :         else {
    1086                 :            :             size_t cur;
    1087                 :            :             Py_ssize_t i;
    1088                 :            : 
    1089         [ #  # ]:          0 :             for (cur = start, i = 0;
    1090                 :            :                  i < slicelen;
    1091                 :          0 :                  cur += step, i++)
    1092                 :            :             {
    1093                 :          0 :                 self->data[cur] = ((char *)vbuf.buf)[i];
    1094                 :            :             }
    1095                 :            :         }
    1096                 :          0 :         PyBuffer_Release(&vbuf);
    1097                 :          0 :         return 0;
    1098                 :            :     }
    1099                 :            :     else {
    1100                 :          0 :         PyErr_SetString(PyExc_TypeError,
    1101                 :            :                         "mmap indices must be integer");
    1102                 :          0 :         return -1;
    1103                 :            :     }
    1104                 :            : }
    1105                 :            : 
    1106                 :            : static PyObject *
    1107                 :            : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
    1108                 :            : 
    1109                 :            : PyDoc_STRVAR(mmap_doc,
    1110                 :            : "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
    1111                 :            : \n\
    1112                 :            : Maps length bytes from the file specified by the file handle fileno,\n\
    1113                 :            : and returns a mmap object.  If length is larger than the current size\n\
    1114                 :            : of the file, the file is extended to contain length bytes.  If length\n\
    1115                 :            : is 0, the maximum length of the map is the current size of the file,\n\
    1116                 :            : except that if the file is empty Windows raises an exception (you cannot\n\
    1117                 :            : create an empty mapping on Windows).\n\
    1118                 :            : \n\
    1119                 :            : Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
    1120                 :            : \n\
    1121                 :            : Maps length bytes from the file specified by the file descriptor fileno,\n\
    1122                 :            : and returns a mmap object.  If length is 0, the maximum length of the map\n\
    1123                 :            : will be the current size of the file when mmap is called.\n\
    1124                 :            : flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
    1125                 :            : private copy-on-write mapping, so changes to the contents of the mmap\n\
    1126                 :            : object will be private to this process, and MAP_SHARED creates a mapping\n\
    1127                 :            : that's shared with all other processes mapping the same areas of the file.\n\
    1128                 :            : The default value is MAP_SHARED.\n\
    1129                 :            : \n\
    1130                 :            : To map anonymous memory, pass -1 as the fileno (both versions).");
    1131                 :            : 
    1132                 :            : 
    1133                 :            : static PyType_Slot mmap_object_slots[] = {
    1134                 :            :     {Py_tp_new, new_mmap_object},
    1135                 :            :     {Py_tp_dealloc, mmap_object_dealloc},
    1136                 :            :     {Py_tp_repr, mmap__repr__method},
    1137                 :            :     {Py_tp_doc, (void *)mmap_doc},
    1138                 :            :     {Py_tp_methods, mmap_object_methods},
    1139                 :            :     {Py_tp_members, mmap_object_members},
    1140                 :            :     {Py_tp_getset, mmap_object_getset},
    1141                 :            :     {Py_tp_getattro, PyObject_GenericGetAttr},
    1142                 :            :     {Py_tp_traverse, mmap_object_traverse},
    1143                 :            : 
    1144                 :            :     /* as sequence */
    1145                 :            :     {Py_sq_length, mmap_length},
    1146                 :            :     {Py_sq_item, mmap_item},
    1147                 :            :     {Py_sq_ass_item, mmap_ass_item},
    1148                 :            : 
    1149                 :            :     /* as mapping */
    1150                 :            :     {Py_mp_length, mmap_length},
    1151                 :            :     {Py_mp_subscript, mmap_subscript},
    1152                 :            :     {Py_mp_ass_subscript, mmap_ass_subscript},
    1153                 :            : 
    1154                 :            :     /* as buffer */
    1155                 :            :     {Py_bf_getbuffer, mmap_buffer_getbuf},
    1156                 :            :     {Py_bf_releasebuffer, mmap_buffer_releasebuf},
    1157                 :            :     {0, NULL},
    1158                 :            : };
    1159                 :            : 
    1160                 :            : static PyType_Spec mmap_object_spec = {
    1161                 :            :     .name = "mmap.mmap",
    1162                 :            :     .basicsize = sizeof(mmap_object),
    1163                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
    1164                 :            :               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
    1165                 :            :     .slots = mmap_object_slots,
    1166                 :            : };
    1167                 :            : 
    1168                 :            : 
    1169                 :            : #ifdef UNIX
    1170                 :            : #ifdef HAVE_LARGEFILE_SUPPORT
    1171                 :            : #define _Py_PARSE_OFF_T "L"
    1172                 :            : #else
    1173                 :            : #define _Py_PARSE_OFF_T "l"
    1174                 :            : #endif
    1175                 :            : 
    1176                 :            : static PyObject *
    1177                 :          0 : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
    1178                 :            : {
    1179                 :            :     struct _Py_stat_struct status;
    1180                 :          0 :     int fstat_result = -1;
    1181                 :            :     mmap_object *m_obj;
    1182                 :            :     Py_ssize_t map_size;
    1183                 :          0 :     off_t offset = 0;
    1184                 :          0 :     int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
    1185                 :          0 :     int devzero = -1;
    1186                 :          0 :     int access = (int)ACCESS_DEFAULT;
    1187                 :            :     static char *keywords[] = {"fileno", "length",
    1188                 :            :                                "flags", "prot",
    1189                 :            :                                "access", "offset", NULL};
    1190                 :            : 
    1191         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
    1192                 :            :                                      &fd, &map_size, &flags, &prot,
    1193                 :            :                                      &access, &offset))
    1194                 :          0 :         return NULL;
    1195         [ #  # ]:          0 :     if (map_size < 0) {
    1196                 :          0 :         PyErr_SetString(PyExc_OverflowError,
    1197                 :            :                         "memory mapped length must be positive");
    1198                 :          0 :         return NULL;
    1199                 :            :     }
    1200         [ #  # ]:          0 :     if (offset < 0) {
    1201                 :          0 :         PyErr_SetString(PyExc_OverflowError,
    1202                 :            :             "memory mapped offset must be positive");
    1203                 :          0 :         return NULL;
    1204                 :            :     }
    1205                 :            : 
    1206         [ #  # ]:          0 :     if ((access != (int)ACCESS_DEFAULT) &&
    1207   [ #  #  #  # ]:          0 :         ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
    1208                 :          0 :         return PyErr_Format(PyExc_ValueError,
    1209                 :            :                             "mmap can't specify both access and flags, prot.");
    1210   [ #  #  #  #  :          0 :     switch ((access_mode)access) {
                      # ]
    1211                 :          0 :     case ACCESS_READ:
    1212                 :          0 :         flags = MAP_SHARED;
    1213                 :          0 :         prot = PROT_READ;
    1214                 :          0 :         break;
    1215                 :          0 :     case ACCESS_WRITE:
    1216                 :          0 :         flags = MAP_SHARED;
    1217                 :          0 :         prot = PROT_READ | PROT_WRITE;
    1218                 :          0 :         break;
    1219                 :          0 :     case ACCESS_COPY:
    1220                 :          0 :         flags = MAP_PRIVATE;
    1221                 :          0 :         prot = PROT_READ | PROT_WRITE;
    1222                 :          0 :         break;
    1223                 :          0 :     case ACCESS_DEFAULT:
    1224                 :            :         /* map prot to access type */
    1225   [ #  #  #  # ]:          0 :         if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
    1226                 :            :             /* ACCESS_DEFAULT */
    1227                 :            :         }
    1228         [ #  # ]:          0 :         else if (prot & PROT_WRITE) {
    1229                 :          0 :             access = ACCESS_WRITE;
    1230                 :            :         }
    1231                 :            :         else {
    1232                 :          0 :             access = ACCESS_READ;
    1233                 :            :         }
    1234                 :          0 :         break;
    1235                 :          0 :     default:
    1236                 :          0 :         return PyErr_Format(PyExc_ValueError,
    1237                 :            :                             "mmap invalid access parameter.");
    1238                 :            :     }
    1239                 :            : 
    1240         [ #  # ]:          0 :     if (PySys_Audit("mmap.__new__", "ini" _Py_PARSE_OFF_T,
    1241                 :            :                     fd, map_size, access, offset) < 0) {
    1242                 :          0 :         return NULL;
    1243                 :            :     }
    1244                 :            : 
    1245                 :            : #ifdef __APPLE__
    1246                 :            :     /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
    1247                 :            :        fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
    1248                 :            :     if (fd != -1)
    1249                 :            :         (void)fcntl(fd, F_FULLFSYNC);
    1250                 :            : #endif
    1251                 :            : 
    1252         [ #  # ]:          0 :     if (fd != -1) {
    1253                 :          0 :         Py_BEGIN_ALLOW_THREADS
    1254                 :          0 :         fstat_result = _Py_fstat_noraise(fd, &status);
    1255                 :          0 :         Py_END_ALLOW_THREADS
    1256                 :            :     }
    1257                 :            : 
    1258   [ #  #  #  #  :          0 :     if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
                   #  # ]
    1259         [ #  # ]:          0 :         if (map_size == 0) {
    1260         [ #  # ]:          0 :             if (status.st_size == 0) {
    1261                 :          0 :                 PyErr_SetString(PyExc_ValueError,
    1262                 :            :                                 "cannot mmap an empty file");
    1263                 :          0 :                 return NULL;
    1264                 :            :             }
    1265         [ #  # ]:          0 :             if (offset >= status.st_size) {
    1266                 :          0 :                 PyErr_SetString(PyExc_ValueError,
    1267                 :            :                                 "mmap offset is greater than file size");
    1268                 :          0 :                 return NULL;
    1269                 :            :             }
    1270                 :            :             if (status.st_size - offset > PY_SSIZE_T_MAX) {
    1271                 :            :                 PyErr_SetString(PyExc_ValueError,
    1272                 :            :                                  "mmap length is too large");
    1273                 :            :                 return NULL;
    1274                 :            :             }
    1275                 :          0 :             map_size = (Py_ssize_t) (status.st_size - offset);
    1276   [ #  #  #  # ]:          0 :         } else if (offset > status.st_size || status.st_size - offset < map_size) {
    1277                 :          0 :             PyErr_SetString(PyExc_ValueError,
    1278                 :            :                             "mmap length is greater than file size");
    1279                 :          0 :             return NULL;
    1280                 :            :         }
    1281                 :            :     }
    1282                 :          0 :     m_obj = (mmap_object *)type->tp_alloc(type, 0);
    1283         [ #  # ]:          0 :     if (m_obj == NULL) {return NULL;}
    1284                 :          0 :     m_obj->data = NULL;
    1285                 :          0 :     m_obj->size = map_size;
    1286                 :          0 :     m_obj->pos = 0;
    1287                 :          0 :     m_obj->weakreflist = NULL;
    1288                 :          0 :     m_obj->exports = 0;
    1289                 :          0 :     m_obj->offset = offset;
    1290         [ #  # ]:          0 :     if (fd == -1) {
    1291                 :          0 :         m_obj->fd = -1;
    1292                 :            :         /* Assume the caller wants to map anonymous memory.
    1293                 :            :            This is the same behaviour as Windows.  mmap.mmap(-1, size)
    1294                 :            :            on both Windows and Unix map anonymous memory.
    1295                 :            :         */
    1296                 :            : #ifdef MAP_ANONYMOUS
    1297                 :            :         /* BSD way to map anonymous memory */
    1298                 :          0 :         flags |= MAP_ANONYMOUS;
    1299                 :            : 
    1300                 :            :         /* VxWorks only supports MAP_ANONYMOUS with MAP_PRIVATE flag */
    1301                 :            : #ifdef __VXWORKS__
    1302                 :            :         flags &= ~MAP_SHARED;
    1303                 :            :         flags |= MAP_PRIVATE;
    1304                 :            : #endif
    1305                 :            : 
    1306                 :            : #else
    1307                 :            :         /* SVR4 method to map anonymous memory is to open /dev/zero */
    1308                 :            :         fd = devzero = _Py_open("/dev/zero", O_RDWR);
    1309                 :            :         if (devzero == -1) {
    1310                 :            :             Py_DECREF(m_obj);
    1311                 :            :             return NULL;
    1312                 :            :         }
    1313                 :            : #endif
    1314                 :            :     }
    1315                 :            :     else {
    1316                 :          0 :         m_obj->fd = _Py_dup(fd);
    1317         [ #  # ]:          0 :         if (m_obj->fd == -1) {
    1318                 :          0 :             Py_DECREF(m_obj);
    1319                 :          0 :             return NULL;
    1320                 :            :         }
    1321                 :            :     }
    1322                 :            : 
    1323                 :          0 :     Py_BEGIN_ALLOW_THREADS
    1324                 :          0 :     m_obj->data = mmap(NULL, map_size, prot, flags, fd, offset);
    1325                 :          0 :     Py_END_ALLOW_THREADS
    1326                 :            : 
    1327         [ #  # ]:          0 :     if (devzero != -1) {
    1328                 :          0 :         close(devzero);
    1329                 :            :     }
    1330                 :            : 
    1331         [ #  # ]:          0 :     if (m_obj->data == (char *)-1) {
    1332                 :          0 :         m_obj->data = NULL;
    1333                 :          0 :         Py_DECREF(m_obj);
    1334                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
    1335                 :          0 :         return NULL;
    1336                 :            :     }
    1337                 :          0 :     m_obj->access = (access_mode)access;
    1338                 :          0 :     return (PyObject *)m_obj;
    1339                 :            : }
    1340                 :            : #endif /* UNIX */
    1341                 :            : 
    1342                 :            : #ifdef MS_WINDOWS
    1343                 :            : 
    1344                 :            : /* A note on sizes and offsets: while the actual map size must hold in a
    1345                 :            :    Py_ssize_t, both the total file size and the start offset can be longer
    1346                 :            :    than a Py_ssize_t, so we use long long which is always 64-bit.
    1347                 :            : */
    1348                 :            : 
    1349                 :            : static PyObject *
    1350                 :            : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
    1351                 :            : {
    1352                 :            :     mmap_object *m_obj;
    1353                 :            :     Py_ssize_t map_size;
    1354                 :            :     long long offset = 0, size;
    1355                 :            :     DWORD off_hi;       /* upper 32 bits of offset */
    1356                 :            :     DWORD off_lo;       /* lower 32 bits of offset */
    1357                 :            :     DWORD size_hi;      /* upper 32 bits of size */
    1358                 :            :     DWORD size_lo;      /* lower 32 bits of size */
    1359                 :            :     const char *tagname = "";
    1360                 :            :     DWORD dwErr = 0;
    1361                 :            :     int fileno;
    1362                 :            :     HANDLE fh = 0;
    1363                 :            :     int access = (access_mode)ACCESS_DEFAULT;
    1364                 :            :     DWORD flProtect, dwDesiredAccess;
    1365                 :            :     static char *keywords[] = { "fileno", "length",
    1366                 :            :                                 "tagname",
    1367                 :            :                                 "access", "offset", NULL };
    1368                 :            : 
    1369                 :            :     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
    1370                 :            :                                      &fileno, &map_size,
    1371                 :            :                                      &tagname, &access, &offset)) {
    1372                 :            :         return NULL;
    1373                 :            :     }
    1374                 :            : 
    1375                 :            :     if (PySys_Audit("mmap.__new__", "iniL",
    1376                 :            :                     fileno, map_size, access, offset) < 0) {
    1377                 :            :         return NULL;
    1378                 :            :     }
    1379                 :            : 
    1380                 :            :     switch((access_mode)access) {
    1381                 :            :     case ACCESS_READ:
    1382                 :            :         flProtect = PAGE_READONLY;
    1383                 :            :         dwDesiredAccess = FILE_MAP_READ;
    1384                 :            :         break;
    1385                 :            :     case ACCESS_DEFAULT:  case ACCESS_WRITE:
    1386                 :            :         flProtect = PAGE_READWRITE;
    1387                 :            :         dwDesiredAccess = FILE_MAP_WRITE;
    1388                 :            :         break;
    1389                 :            :     case ACCESS_COPY:
    1390                 :            :         flProtect = PAGE_WRITECOPY;
    1391                 :            :         dwDesiredAccess = FILE_MAP_COPY;
    1392                 :            :         break;
    1393                 :            :     default:
    1394                 :            :         return PyErr_Format(PyExc_ValueError,
    1395                 :            :                             "mmap invalid access parameter.");
    1396                 :            :     }
    1397                 :            : 
    1398                 :            :     if (map_size < 0) {
    1399                 :            :         PyErr_SetString(PyExc_OverflowError,
    1400                 :            :                         "memory mapped length must be positive");
    1401                 :            :         return NULL;
    1402                 :            :     }
    1403                 :            :     if (offset < 0) {
    1404                 :            :         PyErr_SetString(PyExc_OverflowError,
    1405                 :            :             "memory mapped offset must be positive");
    1406                 :            :         return NULL;
    1407                 :            :     }
    1408                 :            : 
    1409                 :            :     /* assume -1 and 0 both mean invalid filedescriptor
    1410                 :            :        to 'anonymously' map memory.
    1411                 :            :        XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
    1412                 :            :        XXX: Should this code be added?
    1413                 :            :        if (fileno == 0)
    1414                 :            :         PyErr_WarnEx(PyExc_DeprecationWarning,
    1415                 :            :                      "don't use 0 for anonymous memory",
    1416                 :            :                      1);
    1417                 :            :      */
    1418                 :            :     if (fileno != -1 && fileno != 0) {
    1419                 :            :         /* Ensure that fileno is within the CRT's valid range */
    1420                 :            :         fh = _Py_get_osfhandle(fileno);
    1421                 :            :         if (fh == INVALID_HANDLE_VALUE)
    1422                 :            :             return NULL;
    1423                 :            : 
    1424                 :            :         /* Win9x appears to need us seeked to zero */
    1425                 :            :         lseek(fileno, 0, SEEK_SET);
    1426                 :            :     }
    1427                 :            : 
    1428                 :            :     m_obj = (mmap_object *)type->tp_alloc(type, 0);
    1429                 :            :     if (m_obj == NULL)
    1430                 :            :         return NULL;
    1431                 :            :     /* Set every field to an invalid marker, so we can safely
    1432                 :            :        destruct the object in the face of failure */
    1433                 :            :     m_obj->data = NULL;
    1434                 :            :     m_obj->file_handle = INVALID_HANDLE_VALUE;
    1435                 :            :     m_obj->map_handle = NULL;
    1436                 :            :     m_obj->tagname = NULL;
    1437                 :            :     m_obj->offset = offset;
    1438                 :            : 
    1439                 :            :     if (fh) {
    1440                 :            :         /* It is necessary to duplicate the handle, so the
    1441                 :            :            Python code can close it on us */
    1442                 :            :         if (!DuplicateHandle(
    1443                 :            :             GetCurrentProcess(), /* source process handle */
    1444                 :            :             fh, /* handle to be duplicated */
    1445                 :            :             GetCurrentProcess(), /* target proc handle */
    1446                 :            :             (LPHANDLE)&m_obj->file_handle, /* result */
    1447                 :            :             0, /* access - ignored due to options value */
    1448                 :            :             FALSE, /* inherited by child processes? */
    1449                 :            :             DUPLICATE_SAME_ACCESS)) { /* options */
    1450                 :            :             dwErr = GetLastError();
    1451                 :            :             Py_DECREF(m_obj);
    1452                 :            :             PyErr_SetFromWindowsErr(dwErr);
    1453                 :            :             return NULL;
    1454                 :            :         }
    1455                 :            :         if (!map_size) {
    1456                 :            :             DWORD low,high;
    1457                 :            :             low = GetFileSize(fh, &high);
    1458                 :            :             /* low might just happen to have the value INVALID_FILE_SIZE;
    1459                 :            :                so we need to check the last error also. */
    1460                 :            :             if (low == INVALID_FILE_SIZE &&
    1461                 :            :                 (dwErr = GetLastError()) != NO_ERROR) {
    1462                 :            :                 Py_DECREF(m_obj);
    1463                 :            :                 return PyErr_SetFromWindowsErr(dwErr);
    1464                 :            :             }
    1465                 :            : 
    1466                 :            :             size = (((long long) high) << 32) + low;
    1467                 :            :             if (size == 0) {
    1468                 :            :                 PyErr_SetString(PyExc_ValueError,
    1469                 :            :                                 "cannot mmap an empty file");
    1470                 :            :                 Py_DECREF(m_obj);
    1471                 :            :                 return NULL;
    1472                 :            :             }
    1473                 :            :             if (offset >= size) {
    1474                 :            :                 PyErr_SetString(PyExc_ValueError,
    1475                 :            :                                 "mmap offset is greater than file size");
    1476                 :            :                 Py_DECREF(m_obj);
    1477                 :            :                 return NULL;
    1478                 :            :             }
    1479                 :            :             if (size - offset > PY_SSIZE_T_MAX) {
    1480                 :            :                 PyErr_SetString(PyExc_ValueError,
    1481                 :            :                                 "mmap length is too large");
    1482                 :            :                 Py_DECREF(m_obj);
    1483                 :            :                 return NULL;
    1484                 :            :             }
    1485                 :            :             m_obj->size = (Py_ssize_t) (size - offset);
    1486                 :            :         } else {
    1487                 :            :             m_obj->size = map_size;
    1488                 :            :             size = offset + map_size;
    1489                 :            :         }
    1490                 :            :     }
    1491                 :            :     else {
    1492                 :            :         m_obj->size = map_size;
    1493                 :            :         size = offset + map_size;
    1494                 :            :     }
    1495                 :            : 
    1496                 :            :     /* set the initial position */
    1497                 :            :     m_obj->pos = (size_t) 0;
    1498                 :            : 
    1499                 :            :     m_obj->weakreflist = NULL;
    1500                 :            :     m_obj->exports = 0;
    1501                 :            :     /* set the tag name */
    1502                 :            :     if (tagname != NULL && *tagname != '\0') {
    1503                 :            :         m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
    1504                 :            :         if (m_obj->tagname == NULL) {
    1505                 :            :             PyErr_NoMemory();
    1506                 :            :             Py_DECREF(m_obj);
    1507                 :            :             return NULL;
    1508                 :            :         }
    1509                 :            :         strcpy(m_obj->tagname, tagname);
    1510                 :            :     }
    1511                 :            :     else
    1512                 :            :         m_obj->tagname = NULL;
    1513                 :            : 
    1514                 :            :     m_obj->access = (access_mode)access;
    1515                 :            :     size_hi = (DWORD)(size >> 32);
    1516                 :            :     size_lo = (DWORD)(size & 0xFFFFFFFF);
    1517                 :            :     off_hi = (DWORD)(offset >> 32);
    1518                 :            :     off_lo = (DWORD)(offset & 0xFFFFFFFF);
    1519                 :            :     /* For files, it would be sufficient to pass 0 as size.
    1520                 :            :        For anonymous maps, we have to pass the size explicitly. */
    1521                 :            :     m_obj->map_handle = CreateFileMappingA(m_obj->file_handle,
    1522                 :            :                                            NULL,
    1523                 :            :                                            flProtect,
    1524                 :            :                                            size_hi,
    1525                 :            :                                            size_lo,
    1526                 :            :                                            m_obj->tagname);
    1527                 :            :     if (m_obj->map_handle != NULL) {
    1528                 :            :         m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
    1529                 :            :                                              dwDesiredAccess,
    1530                 :            :                                              off_hi,
    1531                 :            :                                              off_lo,
    1532                 :            :                                              m_obj->size);
    1533                 :            :         if (m_obj->data != NULL)
    1534                 :            :             return (PyObject *)m_obj;
    1535                 :            :         else {
    1536                 :            :             dwErr = GetLastError();
    1537                 :            :             CloseHandle(m_obj->map_handle);
    1538                 :            :             m_obj->map_handle = NULL;
    1539                 :            :         }
    1540                 :            :     } else
    1541                 :            :         dwErr = GetLastError();
    1542                 :            :     Py_DECREF(m_obj);
    1543                 :            :     PyErr_SetFromWindowsErr(dwErr);
    1544                 :            :     return NULL;
    1545                 :            : }
    1546                 :            : #endif /* MS_WINDOWS */
    1547                 :            : 
    1548                 :            : static int
    1549                 :          1 : mmap_exec(PyObject *module)
    1550                 :            : {
    1551                 :          1 :     Py_INCREF(PyExc_OSError);
    1552         [ -  + ]:          1 :     if (PyModule_AddObject(module, "error", PyExc_OSError) < 0) {
    1553                 :          0 :         Py_DECREF(PyExc_OSError);
    1554                 :          0 :         return -1;
    1555                 :            :     }
    1556                 :            : 
    1557                 :          1 :     PyObject *mmap_object_type = PyType_FromModuleAndSpec(module,
    1558                 :            :                                                   &mmap_object_spec, NULL);
    1559         [ -  + ]:          1 :     if (mmap_object_type == NULL) {
    1560                 :          0 :         return -1;
    1561                 :            :     }
    1562                 :          1 :     int rc = PyModule_AddType(module, (PyTypeObject *)mmap_object_type);
    1563                 :          1 :     Py_DECREF(mmap_object_type);
    1564         [ -  + ]:          1 :     if (rc < 0) {
    1565                 :          0 :         return -1;
    1566                 :            :     }
    1567                 :            : 
    1568                 :            : #define ADD_INT_MACRO(module, constant)                                     \
    1569                 :            :     do {                                                                    \
    1570                 :            :         if (PyModule_AddIntConstant(module, #constant, constant) < 0) {     \
    1571                 :            :             return -1;                                                      \
    1572                 :            :         }                                                                   \
    1573                 :            :     } while (0)
    1574                 :            : 
    1575                 :            : #ifdef PROT_EXEC
    1576         [ -  + ]:          1 :     ADD_INT_MACRO(module, PROT_EXEC);
    1577                 :            : #endif
    1578                 :            : #ifdef PROT_READ
    1579         [ -  + ]:          1 :     ADD_INT_MACRO(module, PROT_READ);
    1580                 :            : #endif
    1581                 :            : #ifdef PROT_WRITE
    1582         [ -  + ]:          1 :     ADD_INT_MACRO(module, PROT_WRITE);
    1583                 :            : #endif
    1584                 :            : 
    1585                 :            : #ifdef MAP_SHARED
    1586         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_SHARED);
    1587                 :            : #endif
    1588                 :            : #ifdef MAP_PRIVATE
    1589         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_PRIVATE);
    1590                 :            : #endif
    1591                 :            : #ifdef MAP_DENYWRITE
    1592         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_DENYWRITE);
    1593                 :            : #endif
    1594                 :            : #ifdef MAP_EXECUTABLE
    1595         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_EXECUTABLE);
    1596                 :            : #endif
    1597                 :            : #ifdef MAP_ANONYMOUS
    1598         [ -  + ]:          1 :     if (PyModule_AddIntConstant(module, "MAP_ANON", MAP_ANONYMOUS) < 0 ) {
    1599                 :          0 :         return -1;
    1600                 :            :     }
    1601         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_ANONYMOUS);
    1602                 :            : #endif
    1603                 :            : #ifdef MAP_POPULATE
    1604         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_POPULATE);
    1605                 :            : #endif
    1606                 :            : #ifdef MAP_STACK
    1607                 :            :     // Mostly a no-op on Linux and NetBSD, but useful on OpenBSD
    1608                 :            :     // for stack usage (even on x86 arch)
    1609         [ -  + ]:          1 :     ADD_INT_MACRO(module, MAP_STACK);
    1610                 :            : #endif
    1611                 :            : #ifdef MAP_ALIGNED_SUPER
    1612                 :            :     ADD_INT_MACRO(module, MAP_ALIGNED_SUPER);
    1613                 :            : #endif
    1614                 :            : #ifdef MAP_CONCEAL
    1615                 :            :     ADD_INT_MACRO(module, MAP_CONCEAL);
    1616                 :            : #endif
    1617         [ -  + ]:          1 :     if (PyModule_AddIntConstant(module, "PAGESIZE", (long)my_getpagesize()) < 0 ) {
    1618                 :          0 :         return -1;
    1619                 :            :     }
    1620                 :            : 
    1621         [ -  + ]:          1 :     if (PyModule_AddIntConstant(module, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity()) < 0 ) {
    1622                 :          0 :         return -1;
    1623                 :            :     }
    1624                 :            : 
    1625         [ -  + ]:          1 :     ADD_INT_MACRO(module, ACCESS_DEFAULT);
    1626         [ -  + ]:          1 :     ADD_INT_MACRO(module, ACCESS_READ);
    1627         [ -  + ]:          1 :     ADD_INT_MACRO(module, ACCESS_WRITE);
    1628         [ -  + ]:          1 :     ADD_INT_MACRO(module, ACCESS_COPY);
    1629                 :            : 
    1630                 :            : #ifdef HAVE_MADVISE
    1631                 :            :     // Conventional advice values
    1632                 :            : #ifdef MADV_NORMAL
    1633         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_NORMAL);
    1634                 :            : #endif
    1635                 :            : #ifdef MADV_RANDOM
    1636         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_RANDOM);
    1637                 :            : #endif
    1638                 :            : #ifdef MADV_SEQUENTIAL
    1639         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_SEQUENTIAL);
    1640                 :            : #endif
    1641                 :            : #ifdef MADV_WILLNEED
    1642         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_WILLNEED);
    1643                 :            : #endif
    1644                 :            : #ifdef MADV_DONTNEED
    1645         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_DONTNEED);
    1646                 :            : #endif
    1647                 :            : 
    1648                 :            :     // Linux-specific advice values
    1649                 :            : #ifdef MADV_REMOVE
    1650         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_REMOVE);
    1651                 :            : #endif
    1652                 :            : #ifdef MADV_DONTFORK
    1653         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_DONTFORK);
    1654                 :            : #endif
    1655                 :            : #ifdef MADV_DOFORK
    1656         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_DOFORK);
    1657                 :            : #endif
    1658                 :            : #ifdef MADV_HWPOISON
    1659         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_HWPOISON);
    1660                 :            : #endif
    1661                 :            : #ifdef MADV_MERGEABLE
    1662         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_MERGEABLE);
    1663                 :            : #endif
    1664                 :            : #ifdef MADV_UNMERGEABLE
    1665         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_UNMERGEABLE);
    1666                 :            : #endif
    1667                 :            : #ifdef MADV_SOFT_OFFLINE
    1668                 :            :     ADD_INT_MACRO(module, MADV_SOFT_OFFLINE);
    1669                 :            : #endif
    1670                 :            : #ifdef MADV_HUGEPAGE
    1671         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_HUGEPAGE);
    1672                 :            : #endif
    1673                 :            : #ifdef MADV_NOHUGEPAGE
    1674         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_NOHUGEPAGE);
    1675                 :            : #endif
    1676                 :            : #ifdef MADV_DONTDUMP
    1677         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_DONTDUMP);
    1678                 :            : #endif
    1679                 :            : #ifdef MADV_DODUMP
    1680         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_DODUMP);
    1681                 :            : #endif
    1682                 :            : #ifdef MADV_FREE // (Also present on FreeBSD and macOS.)
    1683         [ -  + ]:          1 :     ADD_INT_MACRO(module, MADV_FREE);
    1684                 :            : #endif
    1685                 :            : 
    1686                 :            :     // FreeBSD-specific
    1687                 :            : #ifdef MADV_NOSYNC
    1688                 :            :     ADD_INT_MACRO(module, MADV_NOSYNC);
    1689                 :            : #endif
    1690                 :            : #ifdef MADV_AUTOSYNC
    1691                 :            :     ADD_INT_MACRO(module, MADV_AUTOSYNC);
    1692                 :            : #endif
    1693                 :            : #ifdef MADV_NOCORE
    1694                 :            :     ADD_INT_MACRO(module, MADV_NOCORE);
    1695                 :            : #endif
    1696                 :            : #ifdef MADV_CORE
    1697                 :            :     ADD_INT_MACRO(module, MADV_CORE);
    1698                 :            : #endif
    1699                 :            : #ifdef MADV_PROTECT
    1700                 :            :     ADD_INT_MACRO(module, MADV_PROTECT);
    1701                 :            : #endif
    1702                 :            : 
    1703                 :            :     // Darwin-specific
    1704                 :            : #ifdef MADV_FREE_REUSABLE // (As MADV_FREE but reclaims more faithful for task_info/Activity Monitor...)
    1705                 :            :     ADD_INT_MACRO(module, MADV_FREE_REUSABLE);
    1706                 :            : #endif
    1707                 :            : #ifdef MADV_FREE_REUSE // (Reuse pages previously tagged as reusable)
    1708                 :            :     ADD_INT_MACRO(module, MADV_FREE_REUSE);
    1709                 :            : #endif
    1710                 :            : #endif // HAVE_MADVISE
    1711                 :          1 :     return 0;
    1712                 :            : }
    1713                 :            : 
    1714                 :            : static PyModuleDef_Slot mmap_slots[] = {
    1715                 :            :     {Py_mod_exec, mmap_exec},
    1716                 :            :     {0, NULL}
    1717                 :            : };
    1718                 :            : 
    1719                 :            : static struct PyModuleDef mmapmodule = {
    1720                 :            :     .m_base = PyModuleDef_HEAD_INIT,
    1721                 :            :     .m_name = "mmap",
    1722                 :            :     .m_size = 0,
    1723                 :            :     .m_slots = mmap_slots,
    1724                 :            : };
    1725                 :            : 
    1726                 :            : PyMODINIT_FUNC
    1727                 :          1 : PyInit_mmap(void)
    1728                 :            : {
    1729                 :          1 :     return PyModuleDef_Init(&mmapmodule);
    1730                 :            : }
    1731                 :            : 
    1732                 :            : #endif /* !MS_WINDOWS || MS_WINDOWS_DESKTOP || MS_WINDOWS_GAMES */

Generated by: LCOV version 1.14