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