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