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