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