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