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