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