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 #ifndef Py_BUILD_CORE_BUILTIN
22 # define Py_BUILD_CORE_MODULE 1
23 #endif
24
25 #include <Python.h>
26 #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t()
27 #include "pycore_bytesobject.h" // _PyBytes_Find()
28 #include "pycore_fileutils.h" // _Py_stat_struct
29
30 #include <stddef.h> // offsetof()
31 #ifndef MS_WINDOWS
32 # include <unistd.h> // close()
33 #endif
34
35 #ifndef MS_WINDOWS
36 #define UNIX
37 # ifdef HAVE_FCNTL_H
38 # include <fcntl.h>
39 # endif /* HAVE_FCNTL_H */
40 #endif
41
42 #ifdef MS_WINDOWS
43 #include <windows.h>
44 #include <ntsecapi.h> // LsaNtStatusToWinError
45 static int
my_getpagesize(void)46 my_getpagesize(void)
47 {
48 SYSTEM_INFO si;
49 GetSystemInfo(&si);
50 return si.dwPageSize;
51 }
52
53 static int
my_getallocationgranularity(void)54 my_getallocationgranularity (void)
55 {
56
57 SYSTEM_INFO si;
58 GetSystemInfo(&si);
59 return si.dwAllocationGranularity;
60 }
61
62 #endif
63
64 #ifdef UNIX
65 #include <sys/mman.h>
66 #include <sys/stat.h>
67
68 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
69 static int
my_getpagesize(void)70 my_getpagesize(void)
71 {
72 return sysconf(_SC_PAGESIZE);
73 }
74
75 #define my_getallocationgranularity my_getpagesize
76 #else
77 #define my_getpagesize getpagesize
78 #endif
79
80 #endif /* UNIX */
81
82 #include <string.h>
83
84 #ifdef HAVE_SYS_TYPES_H
85 #include <sys/types.h>
86 #endif /* HAVE_SYS_TYPES_H */
87
88 /* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
89 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
90 # define MAP_ANONYMOUS MAP_ANON
91 #endif
92
93 typedef enum
94 {
95 ACCESS_DEFAULT,
96 ACCESS_READ,
97 ACCESS_WRITE,
98 ACCESS_COPY
99 } access_mode;
100
101 typedef struct {
102 PyObject_HEAD
103 char * data;
104 Py_ssize_t size;
105 Py_ssize_t pos; /* relative to offset */
106 #ifdef MS_WINDOWS
107 long long offset;
108 #else
109 off_t offset;
110 #endif
111 Py_ssize_t exports;
112
113 #ifdef MS_WINDOWS
114 HANDLE map_handle;
115 HANDLE file_handle;
116 wchar_t * tagname;
117 #endif
118
119 #ifdef UNIX
120 int fd;
121 _Bool trackfd;
122 #endif
123
124 PyObject *weakreflist;
125 access_mode access;
126 } mmap_object;
127
128 static int
mmap_object_traverse(mmap_object * m_obj,visitproc visit,void * arg)129 mmap_object_traverse(mmap_object *m_obj, visitproc visit, void *arg)
130 {
131 Py_VISIT(Py_TYPE(m_obj));
132 return 0;
133 }
134
135 static void
mmap_object_dealloc(mmap_object * m_obj)136 mmap_object_dealloc(mmap_object *m_obj)
137 {
138 PyTypeObject *tp = Py_TYPE(m_obj);
139 PyObject_GC_UnTrack(m_obj);
140
141 #ifdef MS_WINDOWS
142 Py_BEGIN_ALLOW_THREADS
143 if (m_obj->data != NULL)
144 UnmapViewOfFile (m_obj->data);
145 if (m_obj->map_handle != NULL)
146 CloseHandle (m_obj->map_handle);
147 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
148 CloseHandle (m_obj->file_handle);
149 Py_END_ALLOW_THREADS
150 if (m_obj->tagname)
151 PyMem_Free(m_obj->tagname);
152 #endif /* MS_WINDOWS */
153
154 #ifdef UNIX
155 Py_BEGIN_ALLOW_THREADS
156 if (m_obj->fd >= 0)
157 (void) close(m_obj->fd);
158 if (m_obj->data!=NULL) {
159 munmap(m_obj->data, m_obj->size);
160 }
161 Py_END_ALLOW_THREADS
162 #endif /* UNIX */
163
164 if (m_obj->weakreflist != NULL)
165 PyObject_ClearWeakRefs((PyObject *) m_obj);
166
167 tp->tp_free(m_obj);
168 Py_DECREF(tp);
169 }
170
171 static PyObject *
mmap_close_method(mmap_object * self,PyObject * Py_UNUSED (ignored))172 mmap_close_method(mmap_object *self, PyObject *Py_UNUSED(ignored))
173 {
174 if (self->exports > 0) {
175 PyErr_SetString(PyExc_BufferError, "cannot close "\
176 "exported pointers exist");
177 return NULL;
178 }
179 #ifdef MS_WINDOWS
180 /* For each resource we maintain, we need to check
181 the value is valid, and if so, free the resource
182 and set the member value to an invalid value so
183 the dealloc does not attempt to resource clearing
184 again.
185 TODO - should we check for errors in the close operations???
186 */
187 HANDLE map_handle = self->map_handle;
188 HANDLE file_handle = self->file_handle;
189 char *data = self->data;
190 self->map_handle = NULL;
191 self->file_handle = INVALID_HANDLE_VALUE;
192 self->data = NULL;
193 Py_BEGIN_ALLOW_THREADS
194 if (data != NULL) {
195 UnmapViewOfFile(data);
196 }
197 if (map_handle != NULL) {
198 CloseHandle(map_handle);
199 }
200 if (file_handle != INVALID_HANDLE_VALUE) {
201 CloseHandle(file_handle);
202 }
203 Py_END_ALLOW_THREADS
204 #endif /* MS_WINDOWS */
205
206 #ifdef UNIX
207 int fd = self->fd;
208 char *data = self->data;
209 self->fd = -1;
210 self->data = NULL;
211 Py_BEGIN_ALLOW_THREADS
212 if (0 <= fd)
213 (void) close(fd);
214 if (data != NULL) {
215 munmap(data, self->size);
216 }
217 Py_END_ALLOW_THREADS
218 #endif
219
220 Py_RETURN_NONE;
221 }
222
223 #ifdef MS_WINDOWS
224 #define CHECK_VALID(err) \
225 do { \
226 if (self->map_handle == NULL) { \
227 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
228 return err; \
229 } \
230 } while (0)
231 #define CHECK_VALID_OR_RELEASE(err, buffer) \
232 do { \
233 if (self->map_handle == NULL) { \
234 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
235 PyBuffer_Release(&(buffer)); \
236 return (err); \
237 } \
238 } while (0)
239 #endif /* MS_WINDOWS */
240
241 #ifdef UNIX
242 #define CHECK_VALID(err) \
243 do { \
244 if (self->data == NULL) { \
245 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
246 return err; \
247 } \
248 } while (0)
249 #define CHECK_VALID_OR_RELEASE(err, buffer) \
250 do { \
251 if (self->data == NULL) { \
252 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
253 PyBuffer_Release(&(buffer)); \
254 return (err); \
255 } \
256 } while (0)
257 #endif /* UNIX */
258
259 #if defined(MS_WINDOWS) && !defined(DONT_USE_SEH)
260 static DWORD
filter_page_exception(EXCEPTION_POINTERS * ptrs,EXCEPTION_RECORD * record)261 filter_page_exception(EXCEPTION_POINTERS *ptrs, EXCEPTION_RECORD *record)
262 {
263 *record = *ptrs->ExceptionRecord;
264 if (record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR ||
265 record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
266 {
267 return EXCEPTION_EXECUTE_HANDLER;
268 }
269 return EXCEPTION_CONTINUE_SEARCH;
270 }
271
272 static DWORD
filter_page_exception_method(mmap_object * self,EXCEPTION_POINTERS * ptrs,EXCEPTION_RECORD * record)273 filter_page_exception_method(mmap_object *self, EXCEPTION_POINTERS *ptrs,
274 EXCEPTION_RECORD *record)
275 {
276 *record = *ptrs->ExceptionRecord;
277 if (record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR ||
278 record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
279 {
280
281 ULONG_PTR address = record->ExceptionInformation[1];
282 if (address >= (ULONG_PTR) self->data &&
283 address < (ULONG_PTR) self->data + (ULONG_PTR) self->size)
284 {
285 return EXCEPTION_EXECUTE_HANDLER;
286 }
287 }
288 return EXCEPTION_CONTINUE_SEARCH;
289 }
290 #endif
291
292 #if defined(MS_WINDOWS) && !defined(DONT_USE_SEH)
293 #define HANDLE_INVALID_MEM(sourcecode) \
294 do { \
295 EXCEPTION_RECORD record; \
296 __try { \
297 sourcecode \
298 } \
299 __except (filter_page_exception(GetExceptionInformation(), &record)) { \
300 assert(record.ExceptionCode == EXCEPTION_IN_PAGE_ERROR || \
301 record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION); \
302 if (record.ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { \
303 NTSTATUS status = (NTSTATUS) record.ExceptionInformation[2]; \
304 ULONG code = LsaNtStatusToWinError(status); \
305 PyErr_SetFromWindowsErr(code); \
306 } \
307 else if (record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { \
308 PyErr_SetFromWindowsErr(ERROR_NOACCESS); \
309 } \
310 return -1; \
311 } \
312 } while (0)
313 #else
314 #define HANDLE_INVALID_MEM(sourcecode) \
315 do { \
316 sourcecode \
317 } while (0)
318 #endif
319
320 #if defined(MS_WINDOWS) && !defined(DONT_USE_SEH)
321 #define HANDLE_INVALID_MEM_METHOD(self, sourcecode) \
322 do { \
323 EXCEPTION_RECORD record; \
324 __try { \
325 sourcecode \
326 } \
327 __except (filter_page_exception_method(self, GetExceptionInformation(), \
328 &record)) { \
329 assert(record.ExceptionCode == EXCEPTION_IN_PAGE_ERROR || \
330 record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION); \
331 if (record.ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { \
332 NTSTATUS status = (NTSTATUS) record.ExceptionInformation[2]; \
333 ULONG code = LsaNtStatusToWinError(status); \
334 PyErr_SetFromWindowsErr(code); \
335 } \
336 else if (record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { \
337 PyErr_SetFromWindowsErr(ERROR_NOACCESS); \
338 } \
339 return -1; \
340 } \
341 } while (0)
342 #else
343 #define HANDLE_INVALID_MEM_METHOD(self, sourcecode) \
344 do { \
345 sourcecode \
346 } while (0)
347 #endif
348
349 int
safe_memcpy(void * dest,const void * src,size_t count)350 safe_memcpy(void *dest, const void *src, size_t count)
351 {
352 HANDLE_INVALID_MEM(
353 memcpy(dest, src, count);
354 );
355 return 0;
356 }
357
358 int
safe_byte_copy(char * dest,const char * src)359 safe_byte_copy(char *dest, const char *src)
360 {
361 HANDLE_INVALID_MEM(
362 *dest = *src;
363 );
364 return 0;
365 }
366
367 int
safe_memchr(char ** out,const void * ptr,int ch,size_t count)368 safe_memchr(char **out, const void *ptr, int ch, size_t count)
369 {
370 HANDLE_INVALID_MEM(
371 *out = (char *) memchr(ptr, ch, count);
372 );
373 return 0;
374 }
375
376 int
safe_memmove(void * dest,const void * src,size_t count)377 safe_memmove(void *dest, const void *src, size_t count)
378 {
379 HANDLE_INVALID_MEM(
380 memmove(dest, src, count);
381 );
382 return 0;
383 }
384
385 int
safe_copy_from_slice(char * dest,const char * src,Py_ssize_t start,Py_ssize_t step,Py_ssize_t slicelen)386 safe_copy_from_slice(char *dest, const char *src, Py_ssize_t start,
387 Py_ssize_t step, Py_ssize_t slicelen)
388 {
389 HANDLE_INVALID_MEM(
390 size_t cur;
391 Py_ssize_t i;
392 for (cur = start, i = 0; i < slicelen; cur += step, i++) {
393 dest[cur] = src[i];
394 }
395 );
396 return 0;
397 }
398
399 int
safe_copy_to_slice(char * dest,const char * src,Py_ssize_t start,Py_ssize_t step,Py_ssize_t slicelen)400 safe_copy_to_slice(char *dest, const char *src, Py_ssize_t start,
401 Py_ssize_t step, Py_ssize_t slicelen)
402 {
403 HANDLE_INVALID_MEM(
404 size_t cur;
405 Py_ssize_t i;
406 for (cur = start, i = 0; i < slicelen; cur += step, i++) {
407 dest[i] = src[cur];
408 }
409 );
410 return 0;
411 }
412
413
414 int
_safe_PyBytes_Find(Py_ssize_t * out,mmap_object * self,const char * haystack,Py_ssize_t len_haystack,const char * needle,Py_ssize_t len_needle,Py_ssize_t offset)415 _safe_PyBytes_Find(Py_ssize_t *out, mmap_object *self, const char *haystack,
416 Py_ssize_t len_haystack, const char *needle,
417 Py_ssize_t len_needle, Py_ssize_t offset)
418 {
419 HANDLE_INVALID_MEM_METHOD(self,
420 *out = _PyBytes_Find(haystack, len_haystack, needle, len_needle, offset);
421 );
422 return 0;
423 }
424
425 int
_safe_PyBytes_ReverseFind(Py_ssize_t * out,mmap_object * self,const char * haystack,Py_ssize_t len_haystack,const char * needle,Py_ssize_t len_needle,Py_ssize_t offset)426 _safe_PyBytes_ReverseFind(Py_ssize_t *out, mmap_object *self,
427 const char *haystack, Py_ssize_t len_haystack,
428 const char *needle, Py_ssize_t len_needle,
429 Py_ssize_t offset)
430 {
431 HANDLE_INVALID_MEM_METHOD(self,
432 *out = _PyBytes_ReverseFind(haystack, len_haystack, needle, len_needle,
433 offset);
434 );
435 return 0;
436 }
437
438 PyObject *
_safe_PyBytes_FromStringAndSize(char * start,size_t num_bytes)439 _safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) {
440 if (num_bytes == 1) {
441 char dest;
442 if (safe_byte_copy(&dest, start) < 0) {
443 return NULL;
444 }
445 else {
446 return PyBytes_FromStringAndSize(&dest, 1);
447 }
448 }
449 else {
450 PyObject *result = PyBytes_FromStringAndSize(NULL, num_bytes);
451 if (result == NULL) {
452 return NULL;
453 }
454 if (safe_memcpy(PyBytes_AS_STRING(result), start, num_bytes) < 0) {
455 Py_CLEAR(result);
456 }
457 return result;
458 }
459 }
460
461 static PyObject *
mmap_read_byte_method(mmap_object * self,PyObject * Py_UNUSED (ignored))462 mmap_read_byte_method(mmap_object *self,
463 PyObject *Py_UNUSED(ignored))
464 {
465 CHECK_VALID(NULL);
466 if (self->pos >= self->size) {
467 PyErr_SetString(PyExc_ValueError, "read byte out of range");
468 return NULL;
469 }
470 char dest;
471 if (safe_byte_copy(&dest, self->data + self->pos) < 0) {
472 return NULL;
473 }
474 self->pos++;
475 return PyLong_FromLong((unsigned char) dest);
476 }
477
478 static PyObject *
mmap_read_line_method(mmap_object * self,PyObject * Py_UNUSED (ignored))479 mmap_read_line_method(mmap_object *self,
480 PyObject *Py_UNUSED(ignored))
481 {
482 Py_ssize_t remaining;
483 char *start, *eol;
484
485 CHECK_VALID(NULL);
486
487 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
488 if (!remaining)
489 return PyBytes_FromString("");
490 start = self->data + self->pos;
491
492 if (safe_memchr(&eol, start, '\n', remaining) < 0) {
493 return NULL;
494 }
495
496 if (!eol)
497 eol = self->data + self->size;
498 else
499 ++eol; /* advance past newline */
500
501 PyObject *result = _safe_PyBytes_FromStringAndSize(start, eol - start);
502 if (result != NULL) {
503 self->pos += (eol - start);
504 }
505 return result;
506 }
507
508 static PyObject *
mmap_read_method(mmap_object * self,PyObject * args)509 mmap_read_method(mmap_object *self,
510 PyObject *args)
511 {
512 Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
513
514 CHECK_VALID(NULL);
515 if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
516 return NULL;
517 CHECK_VALID(NULL);
518
519 /* silently 'adjust' out-of-range requests */
520 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
521 if (num_bytes < 0 || num_bytes > remaining)
522 num_bytes = remaining;
523
524 PyObject *result = _safe_PyBytes_FromStringAndSize(self->data + self->pos,
525 num_bytes);
526 if (result != NULL) {
527 self->pos += num_bytes;
528 }
529 return result;
530 }
531
532 static PyObject *
mmap_gfind(mmap_object * self,PyObject * args,int reverse)533 mmap_gfind(mmap_object *self,
534 PyObject *args,
535 int reverse)
536 {
537 Py_ssize_t start = self->pos;
538 Py_ssize_t end = self->size;
539 Py_buffer view;
540
541 CHECK_VALID(NULL);
542 if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
543 &view, &start, &end)) {
544 return NULL;
545 }
546 else {
547 if (start < 0)
548 start += self->size;
549 if (start < 0)
550 start = 0;
551 else if (start > self->size)
552 start = self->size;
553
554 if (end < 0)
555 end += self->size;
556 if (end < 0)
557 end = 0;
558 else if (end > self->size)
559 end = self->size;
560
561 Py_ssize_t index;
562 PyObject *result;
563 CHECK_VALID_OR_RELEASE(NULL, view);
564 if (end < start) {
565 result = PyLong_FromSsize_t(-1);
566 }
567 else if (reverse) {
568 assert(0 <= start && start <= end && end <= self->size);
569 if (_safe_PyBytes_ReverseFind(&index, self,
570 self->data + start, end - start,
571 view.buf, view.len, start) < 0)
572 {
573 result = NULL;
574 }
575 else {
576 result = PyLong_FromSsize_t(index);
577 }
578 }
579 else {
580 assert(0 <= start && start <= end && end <= self->size);
581 if (_safe_PyBytes_Find(&index, self,
582 self->data + start, end - start,
583 view.buf, view.len, start) < 0)
584 {
585 result = NULL;
586 }
587 else {
588 result = PyLong_FromSsize_t(index);
589 }
590 }
591 PyBuffer_Release(&view);
592 return result;
593 }
594 }
595
596 static PyObject *
mmap_find_method(mmap_object * self,PyObject * args)597 mmap_find_method(mmap_object *self,
598 PyObject *args)
599 {
600 return mmap_gfind(self, args, 0);
601 }
602
603 static PyObject *
mmap_rfind_method(mmap_object * self,PyObject * args)604 mmap_rfind_method(mmap_object *self,
605 PyObject *args)
606 {
607 return mmap_gfind(self, args, 1);
608 }
609
610 static int
is_writable(mmap_object * self)611 is_writable(mmap_object *self)
612 {
613 if (self->access != ACCESS_READ)
614 return 1;
615 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
616 return 0;
617 }
618
619 static int
is_resizeable(mmap_object * self)620 is_resizeable(mmap_object *self)
621 {
622 if (self->exports > 0) {
623 PyErr_SetString(PyExc_BufferError,
624 "mmap can't resize with extant buffers exported.");
625 return 0;
626 }
627 #ifdef UNIX
628 if (!self->trackfd) {
629 PyErr_SetString(PyExc_ValueError,
630 "mmap can't resize with trackfd=False.");
631 return 0;
632 }
633 #endif
634 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
635 return 1;
636 PyErr_Format(PyExc_TypeError,
637 "mmap can't resize a readonly or copy-on-write memory map.");
638 return 0;
639
640 }
641
642
643 static PyObject *
mmap_write_method(mmap_object * self,PyObject * args)644 mmap_write_method(mmap_object *self,
645 PyObject *args)
646 {
647 Py_buffer data;
648
649 CHECK_VALID(NULL);
650 if (!PyArg_ParseTuple(args, "y*:write", &data))
651 return NULL;
652
653 if (!is_writable(self)) {
654 PyBuffer_Release(&data);
655 return NULL;
656 }
657
658 if (self->pos > self->size || self->size - self->pos < data.len) {
659 PyBuffer_Release(&data);
660 PyErr_SetString(PyExc_ValueError, "data out of range");
661 return NULL;
662 }
663
664 CHECK_VALID_OR_RELEASE(NULL, data);
665 PyObject *result;
666 if (safe_memcpy(self->data + self->pos, data.buf, data.len) < 0) {
667 result = NULL;
668 }
669 else {
670 self->pos += data.len;
671 result = PyLong_FromSsize_t(data.len);
672 }
673 PyBuffer_Release(&data);
674 return result;
675 }
676
677 static PyObject *
mmap_write_byte_method(mmap_object * self,PyObject * args)678 mmap_write_byte_method(mmap_object *self,
679 PyObject *args)
680 {
681 char value;
682
683 CHECK_VALID(NULL);
684 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
685 return(NULL);
686
687 if (!is_writable(self))
688 return NULL;
689
690 CHECK_VALID(NULL);
691 if (self->pos >= self->size) {
692 PyErr_SetString(PyExc_ValueError, "write byte out of range");
693 return NULL;
694 }
695
696 if (safe_byte_copy(self->data + self->pos, &value) < 0) {
697 return NULL;
698 }
699 self->pos++;
700 Py_RETURN_NONE;
701 }
702
703 static PyObject *
mmap_size_method(mmap_object * self,PyObject * Py_UNUSED (ignored))704 mmap_size_method(mmap_object *self,
705 PyObject *Py_UNUSED(ignored))
706 {
707 CHECK_VALID(NULL);
708
709 #ifdef MS_WINDOWS
710 if (self->file_handle != INVALID_HANDLE_VALUE) {
711 DWORD low,high;
712 long long size;
713 low = GetFileSize(self->file_handle, &high);
714 if (low == INVALID_FILE_SIZE) {
715 /* It might be that the function appears to have failed,
716 when indeed its size equals INVALID_FILE_SIZE */
717 DWORD error = GetLastError();
718 if (error != NO_ERROR)
719 return PyErr_SetFromWindowsErr(error);
720 }
721 if (!high && low < LONG_MAX)
722 return PyLong_FromLong((long)low);
723 size = (((long long)high)<<32) + low;
724 return PyLong_FromLongLong(size);
725 } else {
726 return PyLong_FromSsize_t(self->size);
727 }
728 #endif /* MS_WINDOWS */
729
730 #ifdef UNIX
731 {
732 struct _Py_stat_struct status;
733 if (_Py_fstat(self->fd, &status) == -1)
734 return NULL;
735 #ifdef HAVE_LARGEFILE_SUPPORT
736 return PyLong_FromLongLong(status.st_size);
737 #else
738 return PyLong_FromLong(status.st_size);
739 #endif
740 }
741 #endif /* UNIX */
742 }
743
744 /* This assumes that you want the entire file mapped,
745 / and when recreating the map will make the new file
746 / have the new size
747 /
748 / Is this really necessary? This could easily be done
749 / from python by just closing and re-opening with the
750 / new size?
751 */
752
753 static PyObject *
mmap_resize_method(mmap_object * self,PyObject * args)754 mmap_resize_method(mmap_object *self,
755 PyObject *args)
756 {
757 Py_ssize_t new_size;
758 CHECK_VALID(NULL);
759 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
760 !is_resizeable(self)) {
761 return NULL;
762 }
763 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
764 PyErr_SetString(PyExc_ValueError, "new size out of range");
765 return NULL;
766 }
767
768 {
769 #ifdef MS_WINDOWS
770 DWORD error = 0, file_resize_error = 0;
771 char* old_data = self->data;
772 LARGE_INTEGER offset, max_size;
773 offset.QuadPart = self->offset;
774 max_size.QuadPart = self->offset + new_size;
775 /* close the file mapping */
776 CloseHandle(self->map_handle);
777 /* if the file mapping still exists, it cannot be resized. */
778 if (self->tagname) {
779 self->map_handle = OpenFileMappingW(FILE_MAP_WRITE, FALSE,
780 self->tagname);
781 if (self->map_handle) {
782 PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE);
783 return NULL;
784 }
785 } else {
786 self->map_handle = NULL;
787 }
788
789 /* if it's not the paging file, unmap the view and resize the file */
790 if (self->file_handle != INVALID_HANDLE_VALUE) {
791 if (!UnmapViewOfFile(self->data)) {
792 return PyErr_SetFromWindowsErr(GetLastError());
793 };
794 self->data = NULL;
795 /* resize the file */
796 if (!SetFilePointerEx(self->file_handle, max_size, NULL,
797 FILE_BEGIN) ||
798 !SetEndOfFile(self->file_handle)) {
799 /* resizing failed. try to remap the file */
800 file_resize_error = GetLastError();
801 max_size.QuadPart = self->size;
802 new_size = self->size;
803 }
804 }
805
806 /* create a new file mapping and map a new view */
807 /* FIXME: call CreateFileMappingW with wchar_t tagname */
808 self->map_handle = CreateFileMappingW(
809 self->file_handle,
810 NULL,
811 PAGE_READWRITE,
812 max_size.HighPart,
813 max_size.LowPart,
814 self->tagname);
815
816 error = GetLastError();
817 /* ERROR_ALREADY_EXISTS implies that between our closing the handle above and
818 calling CreateFileMapping here, someone's created a different mapping with
819 the same name. There's nothing we can usefully do so we invalidate our
820 mapping and error out.
821 */
822 if (error == ERROR_ALREADY_EXISTS) {
823 CloseHandle(self->map_handle);
824 self->map_handle = NULL;
825 }
826 else if (self->map_handle != NULL) {
827 self->data = MapViewOfFile(self->map_handle,
828 FILE_MAP_WRITE,
829 offset.HighPart,
830 offset.LowPart,
831 new_size);
832 if (self->data != NULL) {
833 /* copy the old view if using the paging file */
834 if (self->file_handle == INVALID_HANDLE_VALUE) {
835 memcpy(self->data, old_data,
836 self->size < new_size ? self->size : new_size);
837 if (!UnmapViewOfFile(old_data)) {
838 error = GetLastError();
839 }
840 }
841 self->size = new_size;
842 }
843 else {
844 error = GetLastError();
845 CloseHandle(self->map_handle);
846 self->map_handle = NULL;
847 }
848 }
849
850 if (error) {
851 return PyErr_SetFromWindowsErr(error);
852 return NULL;
853 }
854 /* It's possible for a resize to fail, typically because another mapping
855 is still held against the same underlying file. Even if nothing has
856 failed -- ie we're still returning a valid file mapping -- raise the
857 error as an exception as the resize won't have happened
858 */
859 if (file_resize_error) {
860 PyErr_SetFromWindowsErr(file_resize_error);
861 return NULL;
862 }
863 Py_RETURN_NONE;
864 #endif /* MS_WINDOWS */
865
866 #ifdef UNIX
867 #ifndef HAVE_MREMAP
868 PyErr_SetString(PyExc_SystemError,
869 "mmap: resizing not available--no mremap()");
870 return NULL;
871 #else
872 void *newmap;
873
874 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
875 PyErr_SetFromErrno(PyExc_OSError);
876 return NULL;
877 }
878
879 #ifdef MREMAP_MAYMOVE
880 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
881 #else
882 #if defined(__NetBSD__)
883 newmap = mremap(self->data, self->size, self->data, new_size, 0);
884 #else
885 newmap = mremap(self->data, self->size, new_size, 0);
886 #endif /* __NetBSD__ */
887 #endif
888 if (newmap == (void *)-1)
889 {
890 PyErr_SetFromErrno(PyExc_OSError);
891 return NULL;
892 }
893 self->data = newmap;
894 self->size = new_size;
895 Py_RETURN_NONE;
896 #endif /* HAVE_MREMAP */
897 #endif /* UNIX */
898 }
899 }
900
901 static PyObject *
mmap_tell_method(mmap_object * self,PyObject * Py_UNUSED (ignored))902 mmap_tell_method(mmap_object *self, PyObject *Py_UNUSED(ignored))
903 {
904 CHECK_VALID(NULL);
905 return PyLong_FromSize_t(self->pos);
906 }
907
908 static PyObject *
mmap_flush_method(mmap_object * self,PyObject * args)909 mmap_flush_method(mmap_object *self, PyObject *args)
910 {
911 Py_ssize_t offset = 0;
912 Py_ssize_t size = self->size;
913 CHECK_VALID(NULL);
914 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
915 return NULL;
916 if (size < 0 || offset < 0 || self->size - offset < size) {
917 PyErr_SetString(PyExc_ValueError, "flush values out of range");
918 return NULL;
919 }
920
921 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
922 Py_RETURN_NONE;
923
924 #if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM)
925 if (!FlushViewOfFile(self->data+offset, size)) {
926 PyErr_SetFromWindowsErr(GetLastError());
927 return NULL;
928 }
929 Py_RETURN_NONE;
930 #elif defined(UNIX)
931 /* XXX flags for msync? */
932 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
933 PyErr_SetFromErrno(PyExc_OSError);
934 return NULL;
935 }
936 Py_RETURN_NONE;
937 #else
938 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
939 return NULL;
940 #endif
941 }
942
943 static PyObject *
mmap_seek_method(mmap_object * self,PyObject * args)944 mmap_seek_method(mmap_object *self, PyObject *args)
945 {
946 Py_ssize_t dist;
947 int how=0;
948 CHECK_VALID(NULL);
949 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
950 return NULL;
951 else {
952 Py_ssize_t where;
953 switch (how) {
954 case 0: /* relative to start */
955 where = dist;
956 break;
957 case 1: /* relative to current position */
958 if (PY_SSIZE_T_MAX - self->pos < dist)
959 goto onoutofrange;
960 where = self->pos + dist;
961 break;
962 case 2: /* relative to end */
963 if (PY_SSIZE_T_MAX - self->size < dist)
964 goto onoutofrange;
965 where = self->size + dist;
966 break;
967 default:
968 PyErr_SetString(PyExc_ValueError, "unknown seek type");
969 return NULL;
970 }
971 if (where > self->size || where < 0)
972 goto onoutofrange;
973 self->pos = where;
974 return PyLong_FromSsize_t(self->pos);
975 }
976
977 onoutofrange:
978 PyErr_SetString(PyExc_ValueError, "seek out of range");
979 return NULL;
980 }
981
982 static PyObject *
mmap_seekable_method(mmap_object * self,PyObject * Py_UNUSED (ignored))983 mmap_seekable_method(mmap_object *self, PyObject *Py_UNUSED(ignored))
984 {
985 Py_RETURN_TRUE;
986 }
987
988 static PyObject *
mmap_move_method(mmap_object * self,PyObject * args)989 mmap_move_method(mmap_object *self, PyObject *args)
990 {
991 Py_ssize_t dest, src, cnt;
992 CHECK_VALID(NULL);
993 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
994 !is_writable(self)) {
995 return NULL;
996 } else {
997 /* bounds check the values */
998 if (dest < 0 || src < 0 || cnt < 0)
999 goto bounds;
1000 if (self->size - dest < cnt || self->size - src < cnt)
1001 goto bounds;
1002
1003 CHECK_VALID(NULL);
1004 if (safe_memmove(self->data + dest, self->data + src, cnt) < 0) {
1005 return NULL;
1006 };
1007 Py_RETURN_NONE;
1008
1009 bounds:
1010 PyErr_SetString(PyExc_ValueError,
1011 "source, destination, or count out of range");
1012 return NULL;
1013 }
1014 }
1015
1016 static PyObject *
mmap_closed_get(mmap_object * self,void * Py_UNUSED (ignored))1017 mmap_closed_get(mmap_object *self, void *Py_UNUSED(ignored))
1018 {
1019 #ifdef MS_WINDOWS
1020 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
1021 #elif defined(UNIX)
1022 return PyBool_FromLong(self->data == NULL ? 1 : 0);
1023 #endif
1024 }
1025
1026 static PyObject *
mmap__enter__method(mmap_object * self,PyObject * args)1027 mmap__enter__method(mmap_object *self, PyObject *args)
1028 {
1029 CHECK_VALID(NULL);
1030
1031 return Py_NewRef(self);
1032 }
1033
1034 static PyObject *
mmap__exit__method(PyObject * self,PyObject * args)1035 mmap__exit__method(PyObject *self, PyObject *args)
1036 {
1037 return mmap_close_method((mmap_object *)self, NULL);
1038 }
1039
1040 static PyObject *
mmap__repr__method(PyObject * self)1041 mmap__repr__method(PyObject *self)
1042 {
1043 mmap_object *mobj = (mmap_object *)self;
1044
1045 #ifdef MS_WINDOWS
1046 #define _Py_FORMAT_OFFSET "lld"
1047 if (mobj->map_handle == NULL)
1048 #elif defined(UNIX)
1049 # ifdef HAVE_LARGEFILE_SUPPORT
1050 # define _Py_FORMAT_OFFSET "lld"
1051 # else
1052 # define _Py_FORMAT_OFFSET "ld"
1053 # endif
1054 if (mobj->data == NULL)
1055 #endif
1056 {
1057 return PyUnicode_FromFormat("<%s closed=True>", Py_TYPE(self)->tp_name);
1058 } else {
1059 const char *access_str;
1060
1061 switch (mobj->access) {
1062 case ACCESS_DEFAULT:
1063 access_str = "ACCESS_DEFAULT";
1064 break;
1065 case ACCESS_READ:
1066 access_str = "ACCESS_READ";
1067 break;
1068 case ACCESS_WRITE:
1069 access_str = "ACCESS_WRITE";
1070 break;
1071 case ACCESS_COPY:
1072 access_str = "ACCESS_COPY";
1073 break;
1074 default:
1075 Py_UNREACHABLE();
1076 }
1077
1078 return PyUnicode_FromFormat("<%s closed=False, access=%s, length=%zd, "
1079 "pos=%zd, offset=%" _Py_FORMAT_OFFSET ">",
1080 Py_TYPE(self)->tp_name, access_str,
1081 mobj->size, mobj->pos, mobj->offset);
1082 }
1083 }
1084
1085 #ifdef MS_WINDOWS
1086 static PyObject *
mmap__sizeof__method(mmap_object * self,void * Py_UNUSED (ignored))1087 mmap__sizeof__method(mmap_object *self, void *Py_UNUSED(ignored))
1088 {
1089 size_t res = _PyObject_SIZE(Py_TYPE(self));
1090 if (self->tagname) {
1091 res += (wcslen(self->tagname) + 1) * sizeof(self->tagname[0]);
1092 }
1093 return PyLong_FromSize_t(res);
1094 }
1095 #endif
1096
1097 #if defined(MS_WINDOWS) && defined(Py_DEBUG)
1098 static PyObject *
mmap_protect_method(mmap_object * self,PyObject * args)1099 mmap_protect_method(mmap_object *self, PyObject *args) {
1100 DWORD flNewProtect, flOldProtect;
1101 Py_ssize_t start, length;
1102
1103 CHECK_VALID(NULL);
1104
1105 if (!PyArg_ParseTuple(args, "Inn:protect", &flNewProtect, &start, &length)) {
1106 return NULL;
1107 }
1108
1109 if (!VirtualProtect((void *) (self->data + start), length, flNewProtect,
1110 &flOldProtect))
1111 {
1112 PyErr_SetFromWindowsErr(GetLastError());
1113 return NULL;
1114 }
1115
1116 Py_RETURN_NONE;
1117 }
1118 #endif
1119
1120 #ifdef HAVE_MADVISE
1121 static PyObject *
mmap_madvise_method(mmap_object * self,PyObject * args)1122 mmap_madvise_method(mmap_object *self, PyObject *args)
1123 {
1124 int option;
1125 Py_ssize_t start = 0, length;
1126
1127 CHECK_VALID(NULL);
1128 length = self->size;
1129
1130 if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) {
1131 return NULL;
1132 }
1133
1134 if (start < 0 || start >= self->size) {
1135 PyErr_SetString(PyExc_ValueError, "madvise start out of bounds");
1136 return NULL;
1137 }
1138 if (length < 0) {
1139 PyErr_SetString(PyExc_ValueError, "madvise length invalid");
1140 return NULL;
1141 }
1142 if (PY_SSIZE_T_MAX - start < length) {
1143 PyErr_SetString(PyExc_OverflowError, "madvise length too large");
1144 return NULL;
1145 }
1146
1147 if (start + length > self->size) {
1148 length = self->size - start;
1149 }
1150
1151 CHECK_VALID(NULL);
1152 if (madvise(self->data + start, length, option) != 0) {
1153 PyErr_SetFromErrno(PyExc_OSError);
1154 return NULL;
1155 }
1156
1157 Py_RETURN_NONE;
1158 }
1159 #endif // HAVE_MADVISE
1160
1161 static struct PyMemberDef mmap_object_members[] = {
1162 {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(mmap_object, weakreflist), Py_READONLY},
1163 {NULL},
1164 };
1165
1166 static struct PyMethodDef mmap_object_methods[] = {
1167 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
1168 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
1169 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
1170 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
1171 #ifdef HAVE_MADVISE
1172 {"madvise", (PyCFunction) mmap_madvise_method, METH_VARARGS},
1173 #endif
1174 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
1175 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
1176 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
1177 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
1178 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
1179 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
1180 {"seekable", (PyCFunction) mmap_seekable_method, METH_NOARGS},
1181 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
1182 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
1183 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
1184 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
1185 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
1186 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
1187 #ifdef MS_WINDOWS
1188 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
1189 #ifdef Py_DEBUG
1190 {"_protect", (PyCFunction) mmap_protect_method, METH_VARARGS},
1191 #endif // Py_DEBUG
1192 #endif // MS_WINDOWS
1193 {NULL, NULL} /* sentinel */
1194 };
1195
1196 static PyGetSetDef mmap_object_getset[] = {
1197 {"closed", (getter) mmap_closed_get, NULL, NULL},
1198 {NULL}
1199 };
1200
1201
1202 /* Functions for treating an mmap'ed file as a buffer */
1203
1204 static int
mmap_buffer_getbuf(mmap_object * self,Py_buffer * view,int flags)1205 mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
1206 {
1207 CHECK_VALID(-1);
1208 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
1209 (self->access == ACCESS_READ), flags) < 0)
1210 return -1;
1211 self->exports++;
1212 return 0;
1213 }
1214
1215 static void
mmap_buffer_releasebuf(mmap_object * self,Py_buffer * view)1216 mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
1217 {
1218 self->exports--;
1219 }
1220
1221 static Py_ssize_t
mmap_length(mmap_object * self)1222 mmap_length(mmap_object *self)
1223 {
1224 CHECK_VALID(-1);
1225 return self->size;
1226 }
1227
1228 static PyObject *
mmap_item(mmap_object * self,Py_ssize_t i)1229 mmap_item(mmap_object *self, Py_ssize_t i)
1230 {
1231 CHECK_VALID(NULL);
1232 if (i < 0 || i >= self->size) {
1233 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
1234 return NULL;
1235 }
1236
1237 char dest;
1238 if (safe_byte_copy(&dest, self->data + i) < 0) {
1239 return NULL;
1240 }
1241 return PyBytes_FromStringAndSize(&dest, 1);
1242 }
1243
1244 static PyObject *
mmap_subscript(mmap_object * self,PyObject * item)1245 mmap_subscript(mmap_object *self, PyObject *item)
1246 {
1247 CHECK_VALID(NULL);
1248 if (PyIndex_Check(item)) {
1249 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1250 if (i == -1 && PyErr_Occurred())
1251 return NULL;
1252 if (i < 0)
1253 i += self->size;
1254 if (i < 0 || i >= self->size) {
1255 PyErr_SetString(PyExc_IndexError,
1256 "mmap index out of range");
1257 return NULL;
1258 }
1259 CHECK_VALID(NULL);
1260
1261 char dest;
1262 if (safe_byte_copy(&dest, self->data + i) < 0) {
1263 return NULL;
1264 }
1265 return PyLong_FromLong(Py_CHARMASK(dest));
1266 }
1267 else if (PySlice_Check(item)) {
1268 Py_ssize_t start, stop, step, slicelen;
1269
1270 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
1271 return NULL;
1272 }
1273 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
1274
1275 CHECK_VALID(NULL);
1276 if (slicelen <= 0)
1277 return PyBytes_FromStringAndSize("", 0);
1278 else if (step == 1)
1279 return _safe_PyBytes_FromStringAndSize(self->data + start, slicelen);
1280 else {
1281 char *result_buf = (char *)PyMem_Malloc(slicelen);
1282 PyObject *result;
1283
1284 if (result_buf == NULL)
1285 return PyErr_NoMemory();
1286
1287 if (safe_copy_to_slice(result_buf, self->data, start, step,
1288 slicelen) < 0)
1289 {
1290 result = NULL;
1291 }
1292 else {
1293 result = PyBytes_FromStringAndSize(result_buf, slicelen);
1294 }
1295 PyMem_Free(result_buf);
1296 return result;
1297 }
1298 }
1299 else {
1300 PyErr_SetString(PyExc_TypeError,
1301 "mmap indices must be integers");
1302 return NULL;
1303 }
1304 }
1305
1306 static int
mmap_ass_item(mmap_object * self,Py_ssize_t i,PyObject * v)1307 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
1308 {
1309 const char *buf;
1310
1311 CHECK_VALID(-1);
1312 if (i < 0 || i >= self->size) {
1313 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
1314 return -1;
1315 }
1316 if (v == NULL) {
1317 PyErr_SetString(PyExc_TypeError,
1318 "mmap object doesn't support item deletion");
1319 return -1;
1320 }
1321 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
1322 PyErr_SetString(PyExc_IndexError,
1323 "mmap assignment must be length-1 bytes()");
1324 return -1;
1325 }
1326 if (!is_writable(self))
1327 return -1;
1328 buf = PyBytes_AsString(v);
1329
1330 if (safe_byte_copy(self->data + i, buf) < 0) {
1331 return -1;
1332 }
1333 return 0;
1334 }
1335
1336 static int
mmap_ass_subscript(mmap_object * self,PyObject * item,PyObject * value)1337 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
1338 {
1339 CHECK_VALID(-1);
1340
1341 if (!is_writable(self))
1342 return -1;
1343
1344 if (PyIndex_Check(item)) {
1345 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1346 Py_ssize_t v;
1347
1348 if (i == -1 && PyErr_Occurred())
1349 return -1;
1350 if (i < 0)
1351 i += self->size;
1352 if (i < 0 || i >= self->size) {
1353 PyErr_SetString(PyExc_IndexError,
1354 "mmap index out of range");
1355 return -1;
1356 }
1357 if (value == NULL) {
1358 PyErr_SetString(PyExc_TypeError,
1359 "mmap doesn't support item deletion");
1360 return -1;
1361 }
1362 if (!PyIndex_Check(value)) {
1363 PyErr_SetString(PyExc_TypeError,
1364 "mmap item value must be an int");
1365 return -1;
1366 }
1367 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
1368 if (v == -1 && PyErr_Occurred())
1369 return -1;
1370 if (v < 0 || v > 255) {
1371 PyErr_SetString(PyExc_ValueError,
1372 "mmap item value must be "
1373 "in range(0, 256)");
1374 return -1;
1375 }
1376 CHECK_VALID(-1);
1377
1378 char v_char = (char) v;
1379 if (safe_byte_copy(self->data + i, &v_char) < 0) {
1380 return -1;
1381 }
1382 return 0;
1383 }
1384 else if (PySlice_Check(item)) {
1385 Py_ssize_t start, stop, step, slicelen;
1386 Py_buffer vbuf;
1387
1388 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
1389 return -1;
1390 }
1391 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
1392 if (value == NULL) {
1393 PyErr_SetString(PyExc_TypeError,
1394 "mmap object doesn't support slice deletion");
1395 return -1;
1396 }
1397 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
1398 return -1;
1399 if (vbuf.len != slicelen) {
1400 PyErr_SetString(PyExc_IndexError,
1401 "mmap slice assignment is wrong size");
1402 PyBuffer_Release(&vbuf);
1403 return -1;
1404 }
1405
1406 CHECK_VALID_OR_RELEASE(-1, vbuf);
1407 int result = 0;
1408 if (slicelen == 0) {
1409 }
1410 else if (step == 1) {
1411 if (safe_memcpy(self->data + start, vbuf.buf, slicelen) < 0) {
1412 result = -1;
1413 }
1414 }
1415 else {
1416 if (safe_copy_from_slice(self->data, (char *)vbuf.buf, start, step,
1417 slicelen) < 0)
1418 {
1419 result = -1;
1420 }
1421 }
1422 PyBuffer_Release(&vbuf);
1423 return result;
1424 }
1425 else {
1426 PyErr_SetString(PyExc_TypeError,
1427 "mmap indices must be integer");
1428 return -1;
1429 }
1430 }
1431
1432 static PyObject *
1433 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1434
1435 PyDoc_STRVAR(mmap_doc,
1436 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1437 \n\
1438 Maps length bytes from the file specified by the file handle fileno,\n\
1439 and returns a mmap object. If length is larger than the current size\n\
1440 of the file, the file is extended to contain length bytes. If length\n\
1441 is 0, the maximum length of the map is the current size of the file,\n\
1442 except that if the file is empty Windows raises an exception (you cannot\n\
1443 create an empty mapping on Windows).\n\
1444 \n\
1445 Unix: mmap(fileno, length[, flags[, prot[, access[, offset[, trackfd]]]]])\n\
1446 \n\
1447 Maps length bytes from the file specified by the file descriptor fileno,\n\
1448 and returns a mmap object. If length is 0, the maximum length of the map\n\
1449 will be the current size of the file when mmap is called.\n\
1450 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1451 private copy-on-write mapping, so changes to the contents of the mmap\n\
1452 object will be private to this process, and MAP_SHARED creates a mapping\n\
1453 that's shared with all other processes mapping the same areas of the file.\n\
1454 The default value is MAP_SHARED.\n\
1455 \n\
1456 To map anonymous memory, pass -1 as the fileno (both versions).");
1457
1458
1459 static PyType_Slot mmap_object_slots[] = {
1460 {Py_tp_new, new_mmap_object},
1461 {Py_tp_dealloc, mmap_object_dealloc},
1462 {Py_tp_repr, mmap__repr__method},
1463 {Py_tp_doc, (void *)mmap_doc},
1464 {Py_tp_methods, mmap_object_methods},
1465 {Py_tp_members, mmap_object_members},
1466 {Py_tp_getset, mmap_object_getset},
1467 {Py_tp_getattro, PyObject_GenericGetAttr},
1468 {Py_tp_traverse, mmap_object_traverse},
1469
1470 /* as sequence */
1471 {Py_sq_length, mmap_length},
1472 {Py_sq_item, mmap_item},
1473 {Py_sq_ass_item, mmap_ass_item},
1474
1475 /* as mapping */
1476 {Py_mp_length, mmap_length},
1477 {Py_mp_subscript, mmap_subscript},
1478 {Py_mp_ass_subscript, mmap_ass_subscript},
1479
1480 /* as buffer */
1481 {Py_bf_getbuffer, mmap_buffer_getbuf},
1482 {Py_bf_releasebuffer, mmap_buffer_releasebuf},
1483 {0, NULL},
1484 };
1485
1486 static PyType_Spec mmap_object_spec = {
1487 .name = "mmap.mmap",
1488 .basicsize = sizeof(mmap_object),
1489 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
1490 Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
1491 .slots = mmap_object_slots,
1492 };
1493
1494
1495 #ifdef UNIX
1496 #ifdef HAVE_LARGEFILE_SUPPORT
1497 #define _Py_PARSE_OFF_T "L"
1498 #else
1499 #define _Py_PARSE_OFF_T "l"
1500 #endif
1501
1502 static PyObject *
new_mmap_object(PyTypeObject * type,PyObject * args,PyObject * kwdict)1503 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1504 {
1505 struct _Py_stat_struct status;
1506 int fstat_result = -1;
1507 mmap_object *m_obj;
1508 Py_ssize_t map_size;
1509 off_t offset = 0;
1510 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1511 int devzero = -1;
1512 int access = (int)ACCESS_DEFAULT, trackfd = 1;
1513 static char *keywords[] = {"fileno", "length",
1514 "flags", "prot",
1515 "access", "offset", "trackfd", NULL};
1516
1517 if (!PyArg_ParseTupleAndKeywords(args, kwdict,
1518 "in|iii" _Py_PARSE_OFF_T "$p", keywords,
1519 &fd, &map_size, &flags, &prot,
1520 &access, &offset, &trackfd)) {
1521 return NULL;
1522 }
1523 if (map_size < 0) {
1524 PyErr_SetString(PyExc_OverflowError,
1525 "memory mapped length must be positive");
1526 return NULL;
1527 }
1528 if (offset < 0) {
1529 PyErr_SetString(PyExc_OverflowError,
1530 "memory mapped offset must be positive");
1531 return NULL;
1532 }
1533
1534 if ((access != (int)ACCESS_DEFAULT) &&
1535 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1536 return PyErr_Format(PyExc_ValueError,
1537 "mmap can't specify both access and flags, prot.");
1538 switch ((access_mode)access) {
1539 case ACCESS_READ:
1540 flags = MAP_SHARED;
1541 prot = PROT_READ;
1542 break;
1543 case ACCESS_WRITE:
1544 flags = MAP_SHARED;
1545 prot = PROT_READ | PROT_WRITE;
1546 break;
1547 case ACCESS_COPY:
1548 flags = MAP_PRIVATE;
1549 prot = PROT_READ | PROT_WRITE;
1550 break;
1551 case ACCESS_DEFAULT:
1552 /* map prot to access type */
1553 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1554 /* ACCESS_DEFAULT */
1555 }
1556 else if (prot & PROT_WRITE) {
1557 access = ACCESS_WRITE;
1558 }
1559 else {
1560 access = ACCESS_READ;
1561 }
1562 break;
1563 default:
1564 return PyErr_Format(PyExc_ValueError,
1565 "mmap invalid access parameter.");
1566 }
1567
1568 if (PySys_Audit("mmap.__new__", "ini" _Py_PARSE_OFF_T,
1569 fd, map_size, access, offset) < 0) {
1570 return NULL;
1571 }
1572
1573 #ifdef __APPLE__
1574 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1575 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1576 if (fd != -1)
1577 (void)fcntl(fd, F_FULLFSYNC);
1578 #endif
1579
1580 if (fd != -1) {
1581 Py_BEGIN_ALLOW_THREADS
1582 fstat_result = _Py_fstat_noraise(fd, &status);
1583 Py_END_ALLOW_THREADS
1584 }
1585
1586 if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
1587 if (map_size == 0) {
1588 if (status.st_size == 0) {
1589 PyErr_SetString(PyExc_ValueError,
1590 "cannot mmap an empty file");
1591 return NULL;
1592 }
1593 if (offset >= status.st_size) {
1594 PyErr_SetString(PyExc_ValueError,
1595 "mmap offset is greater than file size");
1596 return NULL;
1597 }
1598 if (status.st_size - offset > PY_SSIZE_T_MAX) {
1599 PyErr_SetString(PyExc_ValueError,
1600 "mmap length is too large");
1601 return NULL;
1602 }
1603 map_size = (Py_ssize_t) (status.st_size - offset);
1604 } else if (offset > status.st_size || status.st_size - offset < map_size) {
1605 PyErr_SetString(PyExc_ValueError,
1606 "mmap length is greater than file size");
1607 return NULL;
1608 }
1609 }
1610 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1611 if (m_obj == NULL) {return NULL;}
1612 m_obj->data = NULL;
1613 m_obj->size = map_size;
1614 m_obj->pos = 0;
1615 m_obj->weakreflist = NULL;
1616 m_obj->exports = 0;
1617 m_obj->offset = offset;
1618 m_obj->trackfd = trackfd;
1619 if (fd == -1) {
1620 m_obj->fd = -1;
1621 /* Assume the caller wants to map anonymous memory.
1622 This is the same behaviour as Windows. mmap.mmap(-1, size)
1623 on both Windows and Unix map anonymous memory.
1624 */
1625 #ifdef MAP_ANONYMOUS
1626 /* BSD way to map anonymous memory */
1627 flags |= MAP_ANONYMOUS;
1628
1629 /* VxWorks only supports MAP_ANONYMOUS with MAP_PRIVATE flag */
1630 #ifdef __VXWORKS__
1631 flags &= ~MAP_SHARED;
1632 flags |= MAP_PRIVATE;
1633 #endif
1634
1635 #else
1636 /* SVR4 method to map anonymous memory is to open /dev/zero */
1637 fd = devzero = _Py_open("/dev/zero", O_RDWR);
1638 if (devzero == -1) {
1639 Py_DECREF(m_obj);
1640 return NULL;
1641 }
1642 #endif
1643 }
1644 else if (trackfd) {
1645 m_obj->fd = _Py_dup(fd);
1646 if (m_obj->fd == -1) {
1647 Py_DECREF(m_obj);
1648 return NULL;
1649 }
1650 }
1651 else {
1652 m_obj->fd = -1;
1653 }
1654
1655 Py_BEGIN_ALLOW_THREADS
1656 m_obj->data = mmap(NULL, map_size, prot, flags, fd, offset);
1657 Py_END_ALLOW_THREADS
1658
1659 int saved_errno = errno;
1660 if (devzero != -1) {
1661 close(devzero);
1662 }
1663
1664 if (m_obj->data == (char *)-1) {
1665 m_obj->data = NULL;
1666 Py_DECREF(m_obj);
1667 errno = saved_errno;
1668 PyErr_SetFromErrno(PyExc_OSError);
1669 return NULL;
1670 }
1671 m_obj->access = (access_mode)access;
1672 return (PyObject *)m_obj;
1673 }
1674 #endif /* UNIX */
1675
1676 #ifdef MS_WINDOWS
1677
1678 /* A note on sizes and offsets: while the actual map size must hold in a
1679 Py_ssize_t, both the total file size and the start offset can be longer
1680 than a Py_ssize_t, so we use long long which is always 64-bit.
1681 */
1682
1683 static PyObject *
new_mmap_object(PyTypeObject * type,PyObject * args,PyObject * kwdict)1684 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1685 {
1686 mmap_object *m_obj;
1687 Py_ssize_t map_size;
1688 long long offset = 0, size;
1689 DWORD off_hi; /* upper 32 bits of offset */
1690 DWORD off_lo; /* lower 32 bits of offset */
1691 DWORD size_hi; /* upper 32 bits of size */
1692 DWORD size_lo; /* lower 32 bits of size */
1693 PyObject *tagname = Py_None;
1694 DWORD dwErr = 0;
1695 int fileno;
1696 HANDLE fh = 0;
1697 int access = (access_mode)ACCESS_DEFAULT;
1698 DWORD flProtect, dwDesiredAccess;
1699 static char *keywords[] = { "fileno", "length",
1700 "tagname",
1701 "access", "offset", NULL };
1702
1703 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|OiL", keywords,
1704 &fileno, &map_size,
1705 &tagname, &access, &offset)) {
1706 return NULL;
1707 }
1708
1709 if (PySys_Audit("mmap.__new__", "iniL",
1710 fileno, map_size, access, offset) < 0) {
1711 return NULL;
1712 }
1713
1714 switch((access_mode)access) {
1715 case ACCESS_READ:
1716 flProtect = PAGE_READONLY;
1717 dwDesiredAccess = FILE_MAP_READ;
1718 break;
1719 case ACCESS_DEFAULT: case ACCESS_WRITE:
1720 flProtect = PAGE_READWRITE;
1721 dwDesiredAccess = FILE_MAP_WRITE;
1722 break;
1723 case ACCESS_COPY:
1724 flProtect = PAGE_WRITECOPY;
1725 dwDesiredAccess = FILE_MAP_COPY;
1726 break;
1727 default:
1728 return PyErr_Format(PyExc_ValueError,
1729 "mmap invalid access parameter.");
1730 }
1731
1732 if (map_size < 0) {
1733 PyErr_SetString(PyExc_OverflowError,
1734 "memory mapped length must be positive");
1735 return NULL;
1736 }
1737 if (offset < 0) {
1738 PyErr_SetString(PyExc_OverflowError,
1739 "memory mapped offset must be positive");
1740 return NULL;
1741 }
1742
1743 /* assume -1 and 0 both mean invalid filedescriptor
1744 to 'anonymously' map memory.
1745 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1746 XXX: Should this code be added?
1747 if (fileno == 0)
1748 PyErr_WarnEx(PyExc_DeprecationWarning,
1749 "don't use 0 for anonymous memory",
1750 1);
1751 */
1752 if (fileno != -1 && fileno != 0) {
1753 /* Ensure that fileno is within the CRT's valid range */
1754 fh = _Py_get_osfhandle(fileno);
1755 if (fh == INVALID_HANDLE_VALUE)
1756 return NULL;
1757
1758 /* Win9x appears to need us seeked to zero */
1759 lseek(fileno, 0, SEEK_SET);
1760 }
1761
1762 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1763 if (m_obj == NULL)
1764 return NULL;
1765 /* Set every field to an invalid marker, so we can safely
1766 destruct the object in the face of failure */
1767 m_obj->data = NULL;
1768 m_obj->file_handle = INVALID_HANDLE_VALUE;
1769 m_obj->map_handle = NULL;
1770 m_obj->tagname = NULL;
1771 m_obj->offset = offset;
1772
1773 if (fh) {
1774 /* It is necessary to duplicate the handle, so the
1775 Python code can close it on us */
1776 if (!DuplicateHandle(
1777 GetCurrentProcess(), /* source process handle */
1778 fh, /* handle to be duplicated */
1779 GetCurrentProcess(), /* target proc handle */
1780 (LPHANDLE)&m_obj->file_handle, /* result */
1781 0, /* access - ignored due to options value */
1782 FALSE, /* inherited by child processes? */
1783 DUPLICATE_SAME_ACCESS)) { /* options */
1784 dwErr = GetLastError();
1785 Py_DECREF(m_obj);
1786 PyErr_SetFromWindowsErr(dwErr);
1787 return NULL;
1788 }
1789 if (!map_size) {
1790 DWORD low,high;
1791 low = GetFileSize(fh, &high);
1792 /* low might just happen to have the value INVALID_FILE_SIZE;
1793 so we need to check the last error also. */
1794 if (low == INVALID_FILE_SIZE &&
1795 (dwErr = GetLastError()) != NO_ERROR) {
1796 Py_DECREF(m_obj);
1797 return PyErr_SetFromWindowsErr(dwErr);
1798 }
1799
1800 size = (((long long) high) << 32) + low;
1801 if (size == 0) {
1802 PyErr_SetString(PyExc_ValueError,
1803 "cannot mmap an empty file");
1804 Py_DECREF(m_obj);
1805 return NULL;
1806 }
1807 if (offset >= size) {
1808 PyErr_SetString(PyExc_ValueError,
1809 "mmap offset is greater than file size");
1810 Py_DECREF(m_obj);
1811 return NULL;
1812 }
1813 if (size - offset > PY_SSIZE_T_MAX) {
1814 PyErr_SetString(PyExc_ValueError,
1815 "mmap length is too large");
1816 Py_DECREF(m_obj);
1817 return NULL;
1818 }
1819 m_obj->size = (Py_ssize_t) (size - offset);
1820 } else {
1821 m_obj->size = map_size;
1822 size = offset + map_size;
1823 }
1824 }
1825 else {
1826 m_obj->size = map_size;
1827 size = offset + map_size;
1828 }
1829
1830 /* set the initial position */
1831 m_obj->pos = (size_t) 0;
1832
1833 m_obj->weakreflist = NULL;
1834 m_obj->exports = 0;
1835 /* set the tag name */
1836 if (!Py_IsNone(tagname)) {
1837 if (!PyUnicode_Check(tagname)) {
1838 Py_DECREF(m_obj);
1839 return PyErr_Format(PyExc_TypeError, "expected str or None for "
1840 "'tagname', not %.200s",
1841 Py_TYPE(tagname)->tp_name);
1842 }
1843 m_obj->tagname = PyUnicode_AsWideCharString(tagname, NULL);
1844 if (m_obj->tagname == NULL) {
1845 Py_DECREF(m_obj);
1846 return NULL;
1847 }
1848 }
1849
1850 m_obj->access = (access_mode)access;
1851 size_hi = (DWORD)(size >> 32);
1852 size_lo = (DWORD)(size & 0xFFFFFFFF);
1853 off_hi = (DWORD)(offset >> 32);
1854 off_lo = (DWORD)(offset & 0xFFFFFFFF);
1855 /* For files, it would be sufficient to pass 0 as size.
1856 For anonymous maps, we have to pass the size explicitly. */
1857 m_obj->map_handle = CreateFileMappingW(m_obj->file_handle,
1858 NULL,
1859 flProtect,
1860 size_hi,
1861 size_lo,
1862 m_obj->tagname);
1863 if (m_obj->map_handle != NULL) {
1864 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1865 dwDesiredAccess,
1866 off_hi,
1867 off_lo,
1868 m_obj->size);
1869 if (m_obj->data != NULL)
1870 return (PyObject *)m_obj;
1871 else {
1872 dwErr = GetLastError();
1873 CloseHandle(m_obj->map_handle);
1874 m_obj->map_handle = NULL;
1875 }
1876 } else
1877 dwErr = GetLastError();
1878 Py_DECREF(m_obj);
1879 PyErr_SetFromWindowsErr(dwErr);
1880 return NULL;
1881 }
1882 #endif /* MS_WINDOWS */
1883
1884 static int
mmap_exec(PyObject * module)1885 mmap_exec(PyObject *module)
1886 {
1887 if (PyModule_AddObjectRef(module, "error", PyExc_OSError) < 0) {
1888 return -1;
1889 }
1890
1891 PyObject *mmap_object_type = PyType_FromModuleAndSpec(module,
1892 &mmap_object_spec, NULL);
1893 if (mmap_object_type == NULL) {
1894 return -1;
1895 }
1896 int rc = PyModule_AddType(module, (PyTypeObject *)mmap_object_type);
1897 Py_DECREF(mmap_object_type);
1898 if (rc < 0) {
1899 return -1;
1900 }
1901
1902 #define ADD_INT_MACRO(module, constant) \
1903 do { \
1904 if (PyModule_AddIntConstant(module, #constant, constant) < 0) { \
1905 return -1; \
1906 } \
1907 } while (0)
1908
1909 #ifdef PROT_EXEC
1910 ADD_INT_MACRO(module, PROT_EXEC);
1911 #endif
1912 #ifdef PROT_READ
1913 ADD_INT_MACRO(module, PROT_READ);
1914 #endif
1915 #ifdef PROT_WRITE
1916 ADD_INT_MACRO(module, PROT_WRITE);
1917 #endif
1918
1919 #ifdef MAP_SHARED
1920 ADD_INT_MACRO(module, MAP_SHARED);
1921 #endif
1922 #ifdef MAP_PRIVATE
1923 ADD_INT_MACRO(module, MAP_PRIVATE);
1924 #endif
1925 #ifdef MAP_DENYWRITE
1926 ADD_INT_MACRO(module, MAP_DENYWRITE);
1927 #endif
1928 #ifdef MAP_EXECUTABLE
1929 ADD_INT_MACRO(module, MAP_EXECUTABLE);
1930 #endif
1931 #ifdef MAP_ANONYMOUS
1932 if (PyModule_AddIntConstant(module, "MAP_ANON", MAP_ANONYMOUS) < 0 ) {
1933 return -1;
1934 }
1935 ADD_INT_MACRO(module, MAP_ANONYMOUS);
1936 #endif
1937 #ifdef MAP_POPULATE
1938 ADD_INT_MACRO(module, MAP_POPULATE);
1939 #endif
1940 #ifdef MAP_STACK
1941 // Mostly a no-op on Linux and NetBSD, but useful on OpenBSD
1942 // for stack usage (even on x86 arch)
1943 ADD_INT_MACRO(module, MAP_STACK);
1944 #endif
1945 #ifdef MAP_ALIGNED_SUPER
1946 ADD_INT_MACRO(module, MAP_ALIGNED_SUPER);
1947 #endif
1948 #ifdef MAP_CONCEAL
1949 ADD_INT_MACRO(module, MAP_CONCEAL);
1950 #endif
1951 #ifdef MAP_NORESERVE
1952 ADD_INT_MACRO(module, MAP_NORESERVE);
1953 #endif
1954 #ifdef MAP_NOEXTEND
1955 ADD_INT_MACRO(module, MAP_NOEXTEND);
1956 #endif
1957 #ifdef MAP_HASSEMAPHORE
1958 ADD_INT_MACRO(module, MAP_HASSEMAPHORE);
1959 #endif
1960 #ifdef MAP_NOCACHE
1961 ADD_INT_MACRO(module, MAP_NOCACHE);
1962 #endif
1963 #ifdef MAP_JIT
1964 ADD_INT_MACRO(module, MAP_JIT);
1965 #endif
1966 #ifdef MAP_RESILIENT_CODESIGN
1967 ADD_INT_MACRO(module, MAP_RESILIENT_CODESIGN);
1968 #endif
1969 #ifdef MAP_RESILIENT_MEDIA
1970 ADD_INT_MACRO(module, MAP_RESILIENT_MEDIA);
1971 #endif
1972 #ifdef MAP_32BIT
1973 ADD_INT_MACRO(module, MAP_32BIT);
1974 #endif
1975 #ifdef MAP_TRANSLATED_ALLOW_EXECUTE
1976 ADD_INT_MACRO(module, MAP_TRANSLATED_ALLOW_EXECUTE);
1977 #endif
1978 #ifdef MAP_UNIX03
1979 ADD_INT_MACRO(module, MAP_UNIX03);
1980 #endif
1981 #ifdef MAP_TPRO
1982 ADD_INT_MACRO(module, MAP_TPRO);
1983 #endif
1984 if (PyModule_AddIntConstant(module, "PAGESIZE", (long)my_getpagesize()) < 0 ) {
1985 return -1;
1986 }
1987
1988 if (PyModule_AddIntConstant(module, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity()) < 0 ) {
1989 return -1;
1990 }
1991
1992 ADD_INT_MACRO(module, ACCESS_DEFAULT);
1993 ADD_INT_MACRO(module, ACCESS_READ);
1994 ADD_INT_MACRO(module, ACCESS_WRITE);
1995 ADD_INT_MACRO(module, ACCESS_COPY);
1996
1997 #ifdef HAVE_MADVISE
1998 // Conventional advice values
1999 #ifdef MADV_NORMAL
2000 ADD_INT_MACRO(module, MADV_NORMAL);
2001 #endif
2002 #ifdef MADV_RANDOM
2003 ADD_INT_MACRO(module, MADV_RANDOM);
2004 #endif
2005 #ifdef MADV_SEQUENTIAL
2006 ADD_INT_MACRO(module, MADV_SEQUENTIAL);
2007 #endif
2008 #ifdef MADV_WILLNEED
2009 ADD_INT_MACRO(module, MADV_WILLNEED);
2010 #endif
2011 #ifdef MADV_DONTNEED
2012 ADD_INT_MACRO(module, MADV_DONTNEED);
2013 #endif
2014
2015 // Linux-specific advice values
2016 #ifdef MADV_REMOVE
2017 ADD_INT_MACRO(module, MADV_REMOVE);
2018 #endif
2019 #ifdef MADV_DONTFORK
2020 ADD_INT_MACRO(module, MADV_DONTFORK);
2021 #endif
2022 #ifdef MADV_DOFORK
2023 ADD_INT_MACRO(module, MADV_DOFORK);
2024 #endif
2025 #ifdef MADV_HWPOISON
2026 ADD_INT_MACRO(module, MADV_HWPOISON);
2027 #endif
2028 #ifdef MADV_MERGEABLE
2029 ADD_INT_MACRO(module, MADV_MERGEABLE);
2030 #endif
2031 #ifdef MADV_UNMERGEABLE
2032 ADD_INT_MACRO(module, MADV_UNMERGEABLE);
2033 #endif
2034 #ifdef MADV_SOFT_OFFLINE
2035 ADD_INT_MACRO(module, MADV_SOFT_OFFLINE);
2036 #endif
2037 #ifdef MADV_HUGEPAGE
2038 ADD_INT_MACRO(module, MADV_HUGEPAGE);
2039 #endif
2040 #ifdef MADV_NOHUGEPAGE
2041 ADD_INT_MACRO(module, MADV_NOHUGEPAGE);
2042 #endif
2043 #ifdef MADV_DONTDUMP
2044 ADD_INT_MACRO(module, MADV_DONTDUMP);
2045 #endif
2046 #ifdef MADV_DODUMP
2047 ADD_INT_MACRO(module, MADV_DODUMP);
2048 #endif
2049 #ifdef MADV_FREE // (Also present on FreeBSD and macOS.)
2050 ADD_INT_MACRO(module, MADV_FREE);
2051 #endif
2052
2053 // FreeBSD-specific
2054 #ifdef MADV_NOSYNC
2055 ADD_INT_MACRO(module, MADV_NOSYNC);
2056 #endif
2057 #ifdef MADV_AUTOSYNC
2058 ADD_INT_MACRO(module, MADV_AUTOSYNC);
2059 #endif
2060 #ifdef MADV_NOCORE
2061 ADD_INT_MACRO(module, MADV_NOCORE);
2062 #endif
2063 #ifdef MADV_CORE
2064 ADD_INT_MACRO(module, MADV_CORE);
2065 #endif
2066 #ifdef MADV_PROTECT
2067 ADD_INT_MACRO(module, MADV_PROTECT);
2068 #endif
2069
2070 // Darwin-specific
2071 #ifdef MADV_FREE_REUSABLE // (As MADV_FREE but reclaims more faithful for task_info/Activity Monitor...)
2072 ADD_INT_MACRO(module, MADV_FREE_REUSABLE);
2073 #endif
2074 #ifdef MADV_FREE_REUSE // (Reuse pages previously tagged as reusable)
2075 ADD_INT_MACRO(module, MADV_FREE_REUSE);
2076 #endif
2077 #endif // HAVE_MADVISE
2078 return 0;
2079 }
2080
2081 static PyModuleDef_Slot mmap_slots[] = {
2082 {Py_mod_exec, mmap_exec},
2083 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
2084 {Py_mod_gil, Py_MOD_GIL_NOT_USED},
2085 {0, NULL}
2086 };
2087
2088 static struct PyModuleDef mmapmodule = {
2089 .m_base = PyModuleDef_HEAD_INIT,
2090 .m_name = "mmap",
2091 .m_size = 0,
2092 .m_slots = mmap_slots,
2093 };
2094
2095 PyMODINIT_FUNC
PyInit_mmap(void)2096 PyInit_mmap(void)
2097 {
2098 return PyModuleDef_Init(&mmapmodule);
2099 }
2100