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