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