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