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