• 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 <stddef.h>               // offsetof()
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     Py_ssize_t  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_CallMethodIdNoArgs(self, &PyId_close);
696 }
697 
698 static PyObject *
mmap__repr__method(PyObject * self)699 mmap__repr__method(PyObject *self)
700 {
701     mmap_object *mobj = (mmap_object *)self;
702 
703 #ifdef MS_WINDOWS
704 #define _Py_FORMAT_OFFSET "lld"
705     if (mobj->map_handle == NULL)
706 #elif defined(UNIX)
707 # ifdef HAVE_LARGEFILE_SUPPORT
708 # define _Py_FORMAT_OFFSET "lld"
709 # else
710 # define _Py_FORMAT_OFFSET "ld"
711 # endif
712     if (mobj->data == NULL)
713 #endif
714     {
715         return PyUnicode_FromFormat("<%s closed=True>", Py_TYPE(self)->tp_name);
716     } else {
717         const char *access_str;
718 
719         switch (mobj->access) {
720             case ACCESS_DEFAULT:
721                 access_str = "ACCESS_DEFAULT";
722                 break;
723             case ACCESS_READ:
724                 access_str = "ACCESS_READ";
725                 break;
726             case ACCESS_WRITE:
727                 access_str = "ACCESS_WRITE";
728                 break;
729             case ACCESS_COPY:
730                 access_str = "ACCESS_COPY";
731                 break;
732             default:
733                 Py_UNREACHABLE();
734         }
735 
736         return PyUnicode_FromFormat("<%s closed=False, access=%s, length=%zd, "
737                                     "pos=%zd, offset=%" _Py_FORMAT_OFFSET ">",
738                                     Py_TYPE(self)->tp_name, access_str,
739                                     mobj->size, mobj->pos, mobj->offset);
740     }
741 }
742 
743 #ifdef MS_WINDOWS
744 static PyObject *
mmap__sizeof__method(mmap_object * self,void * unused)745 mmap__sizeof__method(mmap_object *self, void *unused)
746 {
747     Py_ssize_t res;
748 
749     res = _PyObject_SIZE(Py_TYPE(self));
750     if (self->tagname)
751         res += strlen(self->tagname) + 1;
752     return PyLong_FromSsize_t(res);
753 }
754 #endif
755 
756 #ifdef HAVE_MADVISE
757 static PyObject *
mmap_madvise_method(mmap_object * self,PyObject * args)758 mmap_madvise_method(mmap_object *self, PyObject *args)
759 {
760     int option;
761     Py_ssize_t start = 0, length;
762 
763     CHECK_VALID(NULL);
764     length = self->size;
765 
766     if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) {
767         return NULL;
768     }
769 
770     if (start < 0 || start >= self->size) {
771         PyErr_SetString(PyExc_ValueError, "madvise start out of bounds");
772         return NULL;
773     }
774     if (length < 0) {
775         PyErr_SetString(PyExc_ValueError, "madvise length invalid");
776         return NULL;
777     }
778     if (PY_SSIZE_T_MAX - start < length) {
779         PyErr_SetString(PyExc_OverflowError, "madvise length too large");
780         return NULL;
781     }
782 
783     if (start + length > self->size) {
784         length = self->size - start;
785     }
786 
787     if (madvise(self->data + start, length, option) != 0) {
788         PyErr_SetFromErrno(PyExc_OSError);
789         return NULL;
790     }
791 
792     Py_RETURN_NONE;
793 }
794 #endif // HAVE_MADVISE
795 
796 static struct PyMethodDef mmap_object_methods[] = {
797     {"close",           (PyCFunction) mmap_close_method,        METH_NOARGS},
798     {"find",            (PyCFunction) mmap_find_method,         METH_VARARGS},
799     {"rfind",           (PyCFunction) mmap_rfind_method,        METH_VARARGS},
800     {"flush",           (PyCFunction) mmap_flush_method,        METH_VARARGS},
801 #ifdef HAVE_MADVISE
802     {"madvise",         (PyCFunction) mmap_madvise_method,      METH_VARARGS},
803 #endif
804     {"move",            (PyCFunction) mmap_move_method,         METH_VARARGS},
805     {"read",            (PyCFunction) mmap_read_method,         METH_VARARGS},
806     {"read_byte",       (PyCFunction) mmap_read_byte_method,    METH_NOARGS},
807     {"readline",        (PyCFunction) mmap_read_line_method,    METH_NOARGS},
808     {"resize",          (PyCFunction) mmap_resize_method,       METH_VARARGS},
809     {"seek",            (PyCFunction) mmap_seek_method,         METH_VARARGS},
810     {"size",            (PyCFunction) mmap_size_method,         METH_NOARGS},
811     {"tell",            (PyCFunction) mmap_tell_method,         METH_NOARGS},
812     {"write",           (PyCFunction) mmap_write_method,        METH_VARARGS},
813     {"write_byte",      (PyCFunction) mmap_write_byte_method,   METH_VARARGS},
814     {"__enter__",       (PyCFunction) mmap__enter__method,      METH_NOARGS},
815     {"__exit__",        (PyCFunction) mmap__exit__method,       METH_VARARGS},
816 #ifdef MS_WINDOWS
817     {"__sizeof__",      (PyCFunction) mmap__sizeof__method,     METH_NOARGS},
818 #endif
819     {NULL,         NULL}       /* sentinel */
820 };
821 
822 static PyGetSetDef mmap_object_getset[] = {
823     {"closed", (getter) mmap_closed_get, NULL, NULL},
824     {NULL}
825 };
826 
827 
828 /* Functions for treating an mmap'ed file as a buffer */
829 
830 static int
mmap_buffer_getbuf(mmap_object * self,Py_buffer * view,int flags)831 mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
832 {
833     CHECK_VALID(-1);
834     if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
835                           (self->access == ACCESS_READ), flags) < 0)
836         return -1;
837     self->exports++;
838     return 0;
839 }
840 
841 static void
mmap_buffer_releasebuf(mmap_object * self,Py_buffer * view)842 mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
843 {
844     self->exports--;
845 }
846 
847 static Py_ssize_t
mmap_length(mmap_object * self)848 mmap_length(mmap_object *self)
849 {
850     CHECK_VALID(-1);
851     return self->size;
852 }
853 
854 static PyObject *
mmap_item(mmap_object * self,Py_ssize_t i)855 mmap_item(mmap_object *self, Py_ssize_t i)
856 {
857     CHECK_VALID(NULL);
858     if (i < 0 || i >= self->size) {
859         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
860         return NULL;
861     }
862     return PyBytes_FromStringAndSize(self->data + i, 1);
863 }
864 
865 static PyObject *
mmap_subscript(mmap_object * self,PyObject * item)866 mmap_subscript(mmap_object *self, PyObject *item)
867 {
868     CHECK_VALID(NULL);
869     if (PyIndex_Check(item)) {
870         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
871         if (i == -1 && PyErr_Occurred())
872             return NULL;
873         if (i < 0)
874             i += self->size;
875         if (i < 0 || i >= self->size) {
876             PyErr_SetString(PyExc_IndexError,
877                 "mmap index out of range");
878             return NULL;
879         }
880         return PyLong_FromLong(Py_CHARMASK(self->data[i]));
881     }
882     else if (PySlice_Check(item)) {
883         Py_ssize_t start, stop, step, slicelen;
884 
885         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
886             return NULL;
887         }
888         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
889 
890         if (slicelen <= 0)
891             return PyBytes_FromStringAndSize("", 0);
892         else if (step == 1)
893             return PyBytes_FromStringAndSize(self->data + start,
894                                               slicelen);
895         else {
896             char *result_buf = (char *)PyMem_Malloc(slicelen);
897             size_t cur;
898             Py_ssize_t i;
899             PyObject *result;
900 
901             if (result_buf == NULL)
902                 return PyErr_NoMemory();
903             for (cur = start, i = 0; i < slicelen;
904                  cur += step, i++) {
905                 result_buf[i] = self->data[cur];
906             }
907             result = PyBytes_FromStringAndSize(result_buf,
908                                                 slicelen);
909             PyMem_Free(result_buf);
910             return result;
911         }
912     }
913     else {
914         PyErr_SetString(PyExc_TypeError,
915                         "mmap indices must be integers");
916         return NULL;
917     }
918 }
919 
920 static int
mmap_ass_item(mmap_object * self,Py_ssize_t i,PyObject * v)921 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
922 {
923     const char *buf;
924 
925     CHECK_VALID(-1);
926     if (i < 0 || i >= self->size) {
927         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
928         return -1;
929     }
930     if (v == NULL) {
931         PyErr_SetString(PyExc_TypeError,
932                         "mmap object doesn't support item deletion");
933         return -1;
934     }
935     if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
936         PyErr_SetString(PyExc_IndexError,
937                         "mmap assignment must be length-1 bytes()");
938         return -1;
939     }
940     if (!is_writable(self))
941         return -1;
942     buf = PyBytes_AsString(v);
943     self->data[i] = buf[0];
944     return 0;
945 }
946 
947 static int
mmap_ass_subscript(mmap_object * self,PyObject * item,PyObject * value)948 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
949 {
950     CHECK_VALID(-1);
951 
952     if (!is_writable(self))
953         return -1;
954 
955     if (PyIndex_Check(item)) {
956         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
957         Py_ssize_t v;
958 
959         if (i == -1 && PyErr_Occurred())
960             return -1;
961         if (i < 0)
962             i += self->size;
963         if (i < 0 || i >= self->size) {
964             PyErr_SetString(PyExc_IndexError,
965                             "mmap index out of range");
966             return -1;
967         }
968         if (value == NULL) {
969             PyErr_SetString(PyExc_TypeError,
970                             "mmap doesn't support item deletion");
971             return -1;
972         }
973         if (!PyIndex_Check(value)) {
974             PyErr_SetString(PyExc_TypeError,
975                             "mmap item value must be an int");
976             return -1;
977         }
978         v = PyNumber_AsSsize_t(value, PyExc_TypeError);
979         if (v == -1 && PyErr_Occurred())
980             return -1;
981         if (v < 0 || v > 255) {
982             PyErr_SetString(PyExc_ValueError,
983                             "mmap item value must be "
984                             "in range(0, 256)");
985             return -1;
986         }
987         self->data[i] = (char) v;
988         return 0;
989     }
990     else if (PySlice_Check(item)) {
991         Py_ssize_t start, stop, step, slicelen;
992         Py_buffer vbuf;
993 
994         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
995             return -1;
996         }
997         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
998         if (value == NULL) {
999             PyErr_SetString(PyExc_TypeError,
1000                 "mmap object doesn't support slice deletion");
1001             return -1;
1002         }
1003         if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
1004             return -1;
1005         if (vbuf.len != slicelen) {
1006             PyErr_SetString(PyExc_IndexError,
1007                 "mmap slice assignment is wrong size");
1008             PyBuffer_Release(&vbuf);
1009             return -1;
1010         }
1011 
1012         if (slicelen == 0) {
1013         }
1014         else if (step == 1) {
1015             memcpy(self->data + start, vbuf.buf, slicelen);
1016         }
1017         else {
1018             size_t cur;
1019             Py_ssize_t i;
1020 
1021             for (cur = start, i = 0;
1022                  i < slicelen;
1023                  cur += step, i++)
1024             {
1025                 self->data[cur] = ((char *)vbuf.buf)[i];
1026             }
1027         }
1028         PyBuffer_Release(&vbuf);
1029         return 0;
1030     }
1031     else {
1032         PyErr_SetString(PyExc_TypeError,
1033                         "mmap indices must be integer");
1034         return -1;
1035     }
1036 }
1037 
1038 static PySequenceMethods mmap_as_sequence = {
1039     (lenfunc)mmap_length,            /*sq_length*/
1040     0,                               /*sq_concat*/
1041     0,                               /*sq_repeat*/
1042     (ssizeargfunc)mmap_item,         /*sq_item*/
1043     0,                               /*sq_slice*/
1044     (ssizeobjargproc)mmap_ass_item,  /*sq_ass_item*/
1045     0,                               /*sq_ass_slice*/
1046 };
1047 
1048 static PyMappingMethods mmap_as_mapping = {
1049     (lenfunc)mmap_length,
1050     (binaryfunc)mmap_subscript,
1051     (objobjargproc)mmap_ass_subscript,
1052 };
1053 
1054 static PyBufferProcs mmap_as_buffer = {
1055     (getbufferproc)mmap_buffer_getbuf,
1056     (releasebufferproc)mmap_buffer_releasebuf,
1057 };
1058 
1059 static PyObject *
1060 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1061 
1062 PyDoc_STRVAR(mmap_doc,
1063 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1064 \n\
1065 Maps length bytes from the file specified by the file handle fileno,\n\
1066 and returns a mmap object.  If length is larger than the current size\n\
1067 of the file, the file is extended to contain length bytes.  If length\n\
1068 is 0, the maximum length of the map is the current size of the file,\n\
1069 except that if the file is empty Windows raises an exception (you cannot\n\
1070 create an empty mapping on Windows).\n\
1071 \n\
1072 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1073 \n\
1074 Maps length bytes from the file specified by the file descriptor fileno,\n\
1075 and returns a mmap object.  If length is 0, the maximum length of the map\n\
1076 will be the current size of the file when mmap is called.\n\
1077 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1078 private copy-on-write mapping, so changes to the contents of the mmap\n\
1079 object will be private to this process, and MAP_SHARED creates a mapping\n\
1080 that's shared with all other processes mapping the same areas of the file.\n\
1081 The default value is MAP_SHARED.\n\
1082 \n\
1083 To map anonymous memory, pass -1 as the fileno (both versions).");
1084 
1085 
1086 static PyTypeObject mmap_object_type = {
1087     PyVarObject_HEAD_INIT(NULL, 0)
1088     "mmap.mmap",                                /* tp_name */
1089     sizeof(mmap_object),                        /* tp_basicsize */
1090     0,                                          /* tp_itemsize */
1091     /* methods */
1092     (destructor)mmap_object_dealloc,            /* tp_dealloc */
1093     0,                                          /* tp_vectorcall_offset */
1094     0,                                          /* tp_getattr */
1095     0,                                          /* tp_setattr */
1096     0,                                          /* tp_as_async */
1097     (reprfunc)mmap__repr__method,               /* tp_repr */
1098     0,                                          /* tp_as_number */
1099     &mmap_as_sequence,                          /* tp_as_sequence */
1100     &mmap_as_mapping,                           /* tp_as_mapping */
1101     0,                                          /* tp_hash */
1102     0,                                          /* tp_call */
1103     0,                                          /* tp_str */
1104     PyObject_GenericGetAttr,                    /* tp_getattro */
1105     0,                                          /* tp_setattro */
1106     &mmap_as_buffer,                            /* tp_as_buffer */
1107     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
1108     mmap_doc,                                   /* tp_doc */
1109     0,                                          /* tp_traverse */
1110     0,                                          /* tp_clear */
1111     0,                                          /* tp_richcompare */
1112     offsetof(mmap_object, weakreflist),         /* tp_weaklistoffset */
1113     0,                                          /* tp_iter */
1114     0,                                          /* tp_iternext */
1115     mmap_object_methods,                        /* tp_methods */
1116     0,                                          /* tp_members */
1117     mmap_object_getset,                         /* tp_getset */
1118     0,                                          /* tp_base */
1119     0,                                          /* tp_dict */
1120     0,                                          /* tp_descr_get */
1121     0,                                          /* tp_descr_set */
1122     0,                                          /* tp_dictoffset */
1123     0,                                          /* tp_init */
1124     PyType_GenericAlloc,                        /* tp_alloc */
1125     new_mmap_object,                            /* tp_new */
1126     PyObject_Del,                               /* tp_free */
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         _Py_BEGIN_SUPPRESS_IPH
1382         fh = (HANDLE)_get_osfhandle(fileno);
1383         _Py_END_SUPPRESS_IPH
1384         if (fh==(HANDLE)-1) {
1385             PyErr_SetFromErrno(PyExc_OSError);
1386             return NULL;
1387         }
1388         /* Win9x appears to need us seeked to zero */
1389         lseek(fileno, 0, SEEK_SET);
1390     }
1391 
1392     m_obj = (mmap_object *)type->tp_alloc(type, 0);
1393     if (m_obj == NULL)
1394         return NULL;
1395     /* Set every field to an invalid marker, so we can safely
1396        destruct the object in the face of failure */
1397     m_obj->data = NULL;
1398     m_obj->file_handle = INVALID_HANDLE_VALUE;
1399     m_obj->map_handle = NULL;
1400     m_obj->tagname = NULL;
1401     m_obj->offset = offset;
1402 
1403     if (fh) {
1404         /* It is necessary to duplicate the handle, so the
1405            Python code can close it on us */
1406         if (!DuplicateHandle(
1407             GetCurrentProcess(), /* source process handle */
1408             fh, /* handle to be duplicated */
1409             GetCurrentProcess(), /* target proc handle */
1410             (LPHANDLE)&m_obj->file_handle, /* result */
1411             0, /* access - ignored due to options value */
1412             FALSE, /* inherited by child processes? */
1413             DUPLICATE_SAME_ACCESS)) { /* options */
1414             dwErr = GetLastError();
1415             Py_DECREF(m_obj);
1416             PyErr_SetFromWindowsErr(dwErr);
1417             return NULL;
1418         }
1419         if (!map_size) {
1420             DWORD low,high;
1421             low = GetFileSize(fh, &high);
1422             /* low might just happen to have the value INVALID_FILE_SIZE;
1423                so we need to check the last error also. */
1424             if (low == INVALID_FILE_SIZE &&
1425                 (dwErr = GetLastError()) != NO_ERROR) {
1426                 Py_DECREF(m_obj);
1427                 return PyErr_SetFromWindowsErr(dwErr);
1428             }
1429 
1430             size = (((long long) high) << 32) + low;
1431             if (size == 0) {
1432                 PyErr_SetString(PyExc_ValueError,
1433                                 "cannot mmap an empty file");
1434                 Py_DECREF(m_obj);
1435                 return NULL;
1436             }
1437             if (offset >= size) {
1438                 PyErr_SetString(PyExc_ValueError,
1439                                 "mmap offset is greater than file size");
1440                 Py_DECREF(m_obj);
1441                 return NULL;
1442             }
1443             if (size - offset > PY_SSIZE_T_MAX) {
1444                 PyErr_SetString(PyExc_ValueError,
1445                                 "mmap length is too large");
1446                 Py_DECREF(m_obj);
1447                 return NULL;
1448             }
1449             m_obj->size = (Py_ssize_t) (size - offset);
1450         } else {
1451             m_obj->size = map_size;
1452             size = offset + map_size;
1453         }
1454     }
1455     else {
1456         m_obj->size = map_size;
1457         size = offset + map_size;
1458     }
1459 
1460     /* set the initial position */
1461     m_obj->pos = (size_t) 0;
1462 
1463     m_obj->weakreflist = NULL;
1464     m_obj->exports = 0;
1465     /* set the tag name */
1466     if (tagname != NULL && *tagname != '\0') {
1467         m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1468         if (m_obj->tagname == NULL) {
1469             PyErr_NoMemory();
1470             Py_DECREF(m_obj);
1471             return NULL;
1472         }
1473         strcpy(m_obj->tagname, tagname);
1474     }
1475     else
1476         m_obj->tagname = NULL;
1477 
1478     m_obj->access = (access_mode)access;
1479     size_hi = (DWORD)(size >> 32);
1480     size_lo = (DWORD)(size & 0xFFFFFFFF);
1481     off_hi = (DWORD)(offset >> 32);
1482     off_lo = (DWORD)(offset & 0xFFFFFFFF);
1483     /* For files, it would be sufficient to pass 0 as size.
1484        For anonymous maps, we have to pass the size explicitly. */
1485     m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1486                                           NULL,
1487                                           flProtect,
1488                                           size_hi,
1489                                           size_lo,
1490                                           m_obj->tagname);
1491     if (m_obj->map_handle != NULL) {
1492         m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1493                                              dwDesiredAccess,
1494                                              off_hi,
1495                                              off_lo,
1496                                              m_obj->size);
1497         if (m_obj->data != NULL)
1498             return (PyObject *)m_obj;
1499         else {
1500             dwErr = GetLastError();
1501             CloseHandle(m_obj->map_handle);
1502             m_obj->map_handle = NULL;
1503         }
1504     } else
1505         dwErr = GetLastError();
1506     Py_DECREF(m_obj);
1507     PyErr_SetFromWindowsErr(dwErr);
1508     return NULL;
1509 }
1510 #endif /* MS_WINDOWS */
1511 
1512 static void
setint(PyObject * d,const char * name,long value)1513 setint(PyObject *d, const char *name, long value)
1514 {
1515     PyObject *o = PyLong_FromLong(value);
1516     if (o) {
1517         PyDict_SetItemString(d, name, o);
1518         Py_DECREF(o);
1519     }
1520 }
1521 
1522 
1523 static struct PyModuleDef mmapmodule = {
1524     PyModuleDef_HEAD_INIT,
1525     "mmap",
1526     NULL,
1527     -1,
1528     NULL,
1529     NULL,
1530     NULL,
1531     NULL,
1532     NULL
1533 };
1534 
1535 PyMODINIT_FUNC
PyInit_mmap(void)1536 PyInit_mmap(void)
1537 {
1538     PyObject *dict, *module;
1539 
1540     if (PyType_Ready(&mmap_object_type) < 0)
1541         return NULL;
1542 
1543     module = PyModule_Create(&mmapmodule);
1544     if (module == NULL)
1545         return NULL;
1546     dict = PyModule_GetDict(module);
1547     if (!dict)
1548         return NULL;
1549     PyDict_SetItemString(dict, "error", PyExc_OSError);
1550     PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
1551 #ifdef PROT_EXEC
1552     setint(dict, "PROT_EXEC", PROT_EXEC);
1553 #endif
1554 #ifdef PROT_READ
1555     setint(dict, "PROT_READ", PROT_READ);
1556 #endif
1557 #ifdef PROT_WRITE
1558     setint(dict, "PROT_WRITE", PROT_WRITE);
1559 #endif
1560 
1561 #ifdef MAP_SHARED
1562     setint(dict, "MAP_SHARED", MAP_SHARED);
1563 #endif
1564 #ifdef MAP_PRIVATE
1565     setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1566 #endif
1567 #ifdef MAP_DENYWRITE
1568     setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1569 #endif
1570 #ifdef MAP_EXECUTABLE
1571     setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1572 #endif
1573 #ifdef MAP_ANONYMOUS
1574     setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1575     setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1576 #endif
1577 
1578     setint(dict, "PAGESIZE", (long)my_getpagesize());
1579 
1580     setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1581 
1582     setint(dict, "ACCESS_DEFAULT", ACCESS_DEFAULT);
1583     setint(dict, "ACCESS_READ", ACCESS_READ);
1584     setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1585     setint(dict, "ACCESS_COPY", ACCESS_COPY);
1586 
1587 #ifdef HAVE_MADVISE
1588     // Conventional advice values
1589 #ifdef MADV_NORMAL
1590     setint(dict, "MADV_NORMAL", MADV_NORMAL);
1591 #endif
1592 #ifdef MADV_RANDOM
1593     setint(dict, "MADV_RANDOM", MADV_RANDOM);
1594 #endif
1595 #ifdef MADV_SEQUENTIAL
1596     setint(dict, "MADV_SEQUENTIAL", MADV_SEQUENTIAL);
1597 #endif
1598 #ifdef MADV_WILLNEED
1599     setint(dict, "MADV_WILLNEED", MADV_WILLNEED);
1600 #endif
1601 #ifdef MADV_DONTNEED
1602     setint(dict, "MADV_DONTNEED", MADV_DONTNEED);
1603 #endif
1604 
1605     // Linux-specific advice values
1606 #ifdef MADV_REMOVE
1607     setint(dict, "MADV_REMOVE", MADV_REMOVE);
1608 #endif
1609 #ifdef MADV_DONTFORK
1610     setint(dict, "MADV_DONTFORK", MADV_DONTFORK);
1611 #endif
1612 #ifdef MADV_DOFORK
1613     setint(dict, "MADV_DOFORK", MADV_DOFORK);
1614 #endif
1615 #ifdef MADV_HWPOISON
1616     setint(dict, "MADV_HWPOISON", MADV_HWPOISON);
1617 #endif
1618 #ifdef MADV_MERGEABLE
1619     setint(dict, "MADV_MERGEABLE", MADV_MERGEABLE);
1620 #endif
1621 #ifdef MADV_UNMERGEABLE
1622     setint(dict, "MADV_UNMERGEABLE", MADV_UNMERGEABLE);
1623 #endif
1624 #ifdef MADV_SOFT_OFFLINE
1625     setint(dict, "MADV_SOFT_OFFLINE", MADV_SOFT_OFFLINE);
1626 #endif
1627 #ifdef MADV_HUGEPAGE
1628     setint(dict, "MADV_HUGEPAGE", MADV_HUGEPAGE);
1629 #endif
1630 #ifdef MADV_NOHUGEPAGE
1631     setint(dict, "MADV_NOHUGEPAGE", MADV_NOHUGEPAGE);
1632 #endif
1633 #ifdef MADV_DONTDUMP
1634     setint(dict, "MADV_DONTDUMP", MADV_DONTDUMP);
1635 #endif
1636 #ifdef MADV_DODUMP
1637     setint(dict, "MADV_DODUMP", MADV_DODUMP);
1638 #endif
1639 #ifdef MADV_FREE // (Also present on FreeBSD and macOS.)
1640     setint(dict, "MADV_FREE", MADV_FREE);
1641 #endif
1642 
1643     // FreeBSD-specific
1644 #ifdef MADV_NOSYNC
1645     setint(dict, "MADV_NOSYNC", MADV_NOSYNC);
1646 #endif
1647 #ifdef MADV_AUTOSYNC
1648     setint(dict, "MADV_AUTOSYNC", MADV_AUTOSYNC);
1649 #endif
1650 #ifdef MADV_NOCORE
1651     setint(dict, "MADV_NOCORE", MADV_NOCORE);
1652 #endif
1653 #ifdef MADV_CORE
1654     setint(dict, "MADV_CORE", MADV_CORE);
1655 #endif
1656 #ifdef MADV_PROTECT
1657     setint(dict, "MADV_PROTECT", MADV_PROTECT);
1658 #endif
1659 #endif // HAVE_MADVISE
1660 
1661     return module;
1662 }
1663