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