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