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