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