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