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