blob: 7c15d377cfdf4eeafb3a359b06de69ee503768ec [file] [log] [blame]
Guido van Rossum09fdf072000-03-31 01:17:07 +00001/*
2 / Author: Sam Rushing <rushing@nightmare.com>
Andrew M. Kuchling10f9c072001-11-05 21:25:42 +00003 / Hacked for Unix by AMK
Guido van Rossum09fdf072000-03-31 01:17:07 +00004 / $Id$
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00005
Guido van Rossum8ce8a782007-11-01 19:42:39 +00006 / Modified to support mmap with offset - to map a 'window' of a file
7 / Author: Yotam Medini yotamm@mellanox.co.il
8 /
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00009 / 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 /
Mark Hammond071864a2000-07-30 02:46:26 +000015 / 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
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000018 / ftp://squirl.nightmare.com/pub/python/python-ext.
19*/
20
Martin v. Löwiscfe7e092006-02-17 06:59:14 +000021#define PY_SSIZE_T_CLEAN
Guido van Rossum09fdf072000-03-31 01:17:07 +000022#include <Python.h>
Antoine Pitrouc53204b2013-08-05 23:17:30 +020023#include "structmember.h"
Guido van Rossum09fdf072000-03-31 01:17:07 +000024
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000025#ifndef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000026#define UNIX
Benjamin Petersonf6b5cad2015-08-02 12:15:30 -070027# ifdef HAVE_FCNTL_H
Victor Stinnera6cd0cf2011-05-02 01:05:37 +020028# include <fcntl.h>
Benjamin Petersonf6b5cad2015-08-02 12:15:30 -070029# endif /* HAVE_FCNTL_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000030#endif
31
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000032#ifdef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000033#include <windows.h>
Fred Drake145f96e2000-10-01 17:50:46 +000034static int
35my_getpagesize(void)
36{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000037 SYSTEM_INFO si;
38 GetSystemInfo(&si);
39 return si.dwPageSize;
Fred Drake145f96e2000-10-01 17:50:46 +000040}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000041
42static int
43my_getallocationgranularity (void)
44{
45
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000046 SYSTEM_INFO si;
47 GetSystemInfo(&si);
48 return si.dwAllocationGranularity;
Guido van Rossum8ce8a782007-11-01 19:42:39 +000049}
50
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000051#endif
52
53#ifdef UNIX
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000054#include <sys/mman.h>
Andrew M. Kuchling7b9fb922000-06-17 22:41:22 +000055#include <sys/stat.h>
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000056
Fred Drake145f96e2000-10-01 17:50:46 +000057#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
58static int
59my_getpagesize(void)
60{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000061 return sysconf(_SC_PAGESIZE);
Fred Drake145f96e2000-10-01 17:50:46 +000062}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000063
64#define my_getallocationgranularity my_getpagesize
Fred Drake145f96e2000-10-01 17:50:46 +000065#else
66#define my_getpagesize getpagesize
67#endif
68
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000069#endif /* UNIX */
70
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000071#include <string.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +000072
73#ifdef HAVE_SYS_TYPES_H
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000074#include <sys/types.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +000075#endif /* HAVE_SYS_TYPES_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000076
Neal Norwitz3eaf2b52006-02-16 08:08:54 +000077/* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +000078#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
79# define MAP_ANONYMOUS MAP_ANON
80#endif
81
Tim Peters5ebfd362001-11-13 23:11:19 +000082typedef enum
83{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000084 ACCESS_DEFAULT,
85 ACCESS_READ,
86 ACCESS_WRITE,
87 ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +000088} access_mode;
89
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000090typedef struct {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000091 PyObject_HEAD
92 char * data;
Benjamin Petersoncd04db02016-10-05 21:45:48 -070093 Py_ssize_t size;
94 Py_ssize_t pos; /* relative to offset */
Antoine Pitrou97696cb2011-02-21 23:46:27 +000095#ifdef MS_WINDOWS
Benjamin Petersonaf580df2016-09-06 10:46:49 -070096 long long offset;
Antoine Pitrou97696cb2011-02-21 23:46:27 +000097#else
98 off_t offset;
99#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000100 int exports;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000101
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000102#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000103 HANDLE map_handle;
104 HANDLE file_handle;
105 char * tagname;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000106#endif
107
108#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000109 int fd;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000110#endif
Tim Peters5ebfd362001-11-13 23:11:19 +0000111
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200112 PyObject *weakreflist;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000113 access_mode access;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000114} mmap_object;
115
Tim Peters5ebfd362001-11-13 23:11:19 +0000116
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000117static void
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000118mmap_object_dealloc(mmap_object *m_obj)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000119{
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000120#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000121 if (m_obj->data != NULL)
122 UnmapViewOfFile (m_obj->data);
123 if (m_obj->map_handle != NULL)
124 CloseHandle (m_obj->map_handle);
125 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
126 CloseHandle (m_obj->file_handle);
127 if (m_obj->tagname)
128 PyMem_Free(m_obj->tagname);
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000129#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000130
131#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000132 if (m_obj->fd >= 0)
133 (void) close(m_obj->fd);
134 if (m_obj->data!=NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000135 munmap(m_obj->data, m_obj->size);
136 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000137#endif /* UNIX */
138
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200139 if (m_obj->weakreflist != NULL)
140 PyObject_ClearWeakRefs((PyObject *) m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000141 Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000142}
143
144static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000145mmap_close_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000146{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000147 if (self->exports > 0) {
148 PyErr_SetString(PyExc_BufferError, "cannot close "\
149 "exported pointers exist");
150 return NULL;
151 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000152#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000153 /* For each resource we maintain, we need to check
154 the value is valid, and if so, free the resource
155 and set the member value to an invalid value so
156 the dealloc does not attempt to resource clearing
157 again.
158 TODO - should we check for errors in the close operations???
159 */
160 if (self->data != NULL) {
161 UnmapViewOfFile(self->data);
162 self->data = NULL;
163 }
164 if (self->map_handle != NULL) {
165 CloseHandle(self->map_handle);
166 self->map_handle = NULL;
167 }
168 if (self->file_handle != INVALID_HANDLE_VALUE) {
169 CloseHandle(self->file_handle);
170 self->file_handle = INVALID_HANDLE_VALUE;
171 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000172#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000173
174#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000175 if (0 <= self->fd)
176 (void) close(self->fd);
177 self->fd = -1;
178 if (self->data != NULL) {
179 munmap(self->data, self->size);
180 self->data = NULL;
181 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000182#endif
183
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200184 Py_RETURN_NONE;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000185}
186
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000187#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000188#define CHECK_VALID(err) \
189do { \
190 if (self->map_handle == NULL) { \
191 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
192 return err; \
193 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000194} while (0)
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000195#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000196
197#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000198#define CHECK_VALID(err) \
199do { \
200 if (self->data == NULL) { \
201 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
202 return err; \
203 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000204} while (0)
205#endif /* UNIX */
206
207static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000208mmap_read_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000209 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000210{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000211 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700212 if (self->pos >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000213 PyErr_SetString(PyExc_ValueError, "read byte out of range");
214 return NULL;
215 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700216 return PyLong_FromLong((unsigned char)self->data[self->pos++]);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000217}
218
219static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000220mmap_read_line_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000221 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000222{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700223 Py_ssize_t remaining;
224 char *start, *eol;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000225 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000226
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000227 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000228
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700229 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
230 if (!remaining)
231 return PyBytes_FromString("");
232 start = self->data + self->pos;
233 eol = memchr(start, '\n', remaining);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000234 if (!eol)
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700235 eol = self->data + self->size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000236 else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700237 ++eol; /* advance past newline */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000238 result = PyBytes_FromStringAndSize(start, (eol - start));
239 self->pos += (eol - start);
240 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000241}
242
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200243/* Basically the "n" format code with the ability to turn None into -1. */
244static int
245mmap_convert_ssize_t(PyObject *obj, void *result) {
246 Py_ssize_t limit;
247 if (obj == Py_None) {
248 limit = -1;
249 }
Oren Milman00425102017-03-13 00:37:05 +0200250 else if (PyIndex_Check(obj)) {
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200251 limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
Oren Milman00425102017-03-13 00:37:05 +0200252 if (limit == -1 && PyErr_Occurred()) {
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200253 return 0;
Oren Milman00425102017-03-13 00:37:05 +0200254 }
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200255 }
256 else {
257 PyErr_Format(PyExc_TypeError,
Oren Milman00425102017-03-13 00:37:05 +0200258 "argument should be integer or None, not '%.200s'",
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200259 Py_TYPE(obj)->tp_name);
260 return 0;
261 }
262 *((Py_ssize_t *)result) = limit;
263 return 1;
264}
265
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000266static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000267mmap_read_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000268 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000269{
Benjamin Peterson8f1cdc62016-10-05 23:32:09 -0700270 Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000271 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000272
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000273 CHECK_VALID(NULL);
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200274 if (!PyArg_ParseTuple(args, "|O&:read", mmap_convert_ssize_t, &num_bytes))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000275 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000276
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000277 /* silently 'adjust' out-of-range requests */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700278 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
279 if (num_bytes < 0 || num_bytes > remaining)
280 num_bytes = remaining;
281 result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000282 self->pos += num_bytes;
283 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000284}
285
286static PyObject *
Georg Brandlfceab5a2008-01-19 20:08:23 +0000287mmap_gfind(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000288 PyObject *args,
289 int reverse)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000290{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000291 Py_ssize_t start = self->pos;
292 Py_ssize_t end = self->size;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200293 Py_buffer view;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000294
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000295 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200296 if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
297 &view, &start, &end)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000298 return NULL;
299 } else {
300 const char *p, *start_p, *end_p;
301 int sign = reverse ? -1 : 1;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200302 const char *needle = view.buf;
303 Py_ssize_t len = view.len;
Greg Stein834f4dd2001-05-14 09:32:26 +0000304
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000305 if (start < 0)
306 start += self->size;
307 if (start < 0)
308 start = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700309 else if (start > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000310 start = self->size;
Greg Stein834f4dd2001-05-14 09:32:26 +0000311
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000312 if (end < 0)
313 end += self->size;
314 if (end < 0)
315 end = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700316 else if (end > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000317 end = self->size;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000318
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000319 start_p = self->data + start;
320 end_p = self->data + end;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000321
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000322 for (p = (reverse ? end_p - len : start_p);
323 (p >= start_p) && (p + len <= end_p); p += sign) {
324 Py_ssize_t i;
325 for (i = 0; i < len && needle[i] == p[i]; ++i)
326 /* nothing */;
327 if (i == len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200328 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000329 return PyLong_FromSsize_t(p - self->data);
330 }
331 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200332 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000333 return PyLong_FromLong(-1);
334 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000335}
336
Georg Brandlfceab5a2008-01-19 20:08:23 +0000337static PyObject *
338mmap_find_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000339 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000340{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000341 return mmap_gfind(self, args, 0);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000342}
343
344static PyObject *
345mmap_rfind_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000346 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000347{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000348 return mmap_gfind(self, args, 1);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000349}
350
Tim Petersec0a5f02006-02-16 23:47:20 +0000351static int
Sean Reifscheider54cf12b2007-09-17 17:55:36 +0000352is_writable(mmap_object *self)
Tim Peters5ebfd362001-11-13 23:11:19 +0000353{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000354 if (self->access != ACCESS_READ)
355 return 1;
356 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
357 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000358}
359
Tim Petersec0a5f02006-02-16 23:47:20 +0000360static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000361is_resizeable(mmap_object *self)
362{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000363 if (self->exports > 0) {
364 PyErr_SetString(PyExc_BufferError,
365 "mmap can't resize with extant buffers exported.");
366 return 0;
367 }
368 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
369 return 1;
370 PyErr_Format(PyExc_TypeError,
371 "mmap can't resize a readonly or copy-on-write memory map.");
372 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000373}
374
375
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000376static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000377mmap_write_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000378 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000379{
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200380 Py_buffer data;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000381
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000382 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200383 if (!PyArg_ParseTuple(args, "y*:write", &data))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000384 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000385
Benjamin Peterson37768362016-10-05 23:29:07 -0700386 if (!is_writable(self)) {
387 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700388 return NULL;
Benjamin Peterson37768362016-10-05 23:29:07 -0700389 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700390
391 if (self->pos > self->size || self->size - self->pos < data.len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200392 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700393 PyErr_SetString(PyExc_ValueError, "data out of range");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000394 return NULL;
395 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200396
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700397 memcpy(&self->data[self->pos], data.buf, data.len);
398 self->pos += data.len;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200399 PyBuffer_Release(&data);
Benjamin Peterson87845bc2016-10-05 22:54:19 -0700400 return PyLong_FromSsize_t(data.len);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000401}
402
403static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000404mmap_write_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000406{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000407 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000408
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000409 CHECK_VALID(NULL);
410 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
411 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000412
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000413 if (!is_writable(self))
414 return NULL;
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000415
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000416 if (self->pos < self->size) {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700417 self->data[self->pos++] = value;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200418 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000419 }
420 else {
421 PyErr_SetString(PyExc_ValueError, "write byte out of range");
422 return NULL;
423 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000424}
Tim Petersec0a5f02006-02-16 23:47:20 +0000425
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000426static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000427mmap_size_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000428 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000429{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000430 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000431
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000432#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000433 if (self->file_handle != INVALID_HANDLE_VALUE) {
434 DWORD low,high;
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700435 long long size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000436 low = GetFileSize(self->file_handle, &high);
437 if (low == INVALID_FILE_SIZE) {
438 /* It might be that the function appears to have failed,
439 when indeed its size equals INVALID_FILE_SIZE */
440 DWORD error = GetLastError();
441 if (error != NO_ERROR)
442 return PyErr_SetFromWindowsErr(error);
443 }
444 if (!high && low < LONG_MAX)
445 return PyLong_FromLong((long)low);
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700446 size = (((long long)high)<<32) + low;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000447 return PyLong_FromLongLong(size);
448 } else {
449 return PyLong_FromSsize_t(self->size);
450 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000451#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000452
453#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000454 {
Victor Stinnere134a7f2015-03-30 10:09:31 +0200455 struct _Py_stat_struct status;
456 if (_Py_fstat(self->fd, &status) == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000457 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000458#ifdef HAVE_LARGEFILE_SUPPORT
Victor Stinnere134a7f2015-03-30 10:09:31 +0200459 return PyLong_FromLongLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000460#else
Victor Stinnere134a7f2015-03-30 10:09:31 +0200461 return PyLong_FromLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000462#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000463 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000464#endif /* UNIX */
465}
466
467/* This assumes that you want the entire file mapped,
468 / and when recreating the map will make the new file
469 / have the new size
470 /
471 / Is this really necessary? This could easily be done
472 / from python by just closing and re-opening with the
473 / new size?
474 */
475
476static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000477mmap_resize_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000478 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000479{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000480 Py_ssize_t new_size;
481 CHECK_VALID(NULL);
482 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
483 !is_resizeable(self)) {
484 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700485 }
486 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
487 PyErr_SetString(PyExc_ValueError, "new size out of range");
488 return NULL;
489 }
490
491 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000492#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000493 DWORD dwErrCode = 0;
494 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
495 /* First, unmap the file view */
496 UnmapViewOfFile(self->data);
497 self->data = NULL;
498 /* Close the mapping object */
499 CloseHandle(self->map_handle);
500 self->map_handle = NULL;
501 /* Move to the desired EOF position */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000502 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
503 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
504 off_hi = (DWORD)(self->offset >> 32);
505 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000506 SetFilePointer(self->file_handle,
507 newSizeLow, &newSizeHigh, FILE_BEGIN);
508 /* Change the size of the file */
509 SetEndOfFile(self->file_handle);
510 /* Create another mapping object and remap the file view */
511 self->map_handle = CreateFileMapping(
512 self->file_handle,
513 NULL,
514 PAGE_READWRITE,
515 0,
516 0,
517 self->tagname);
518 if (self->map_handle != NULL) {
519 self->data = (char *) MapViewOfFile(self->map_handle,
520 FILE_MAP_WRITE,
521 off_hi,
522 off_lo,
523 new_size);
524 if (self->data != NULL) {
525 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200526 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000527 } 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;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000537#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000538
539#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000540#ifndef HAVE_MREMAP
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000541 PyErr_SetString(PyExc_SystemError,
542 "mmap: resizing not available--no mremap()");
543 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000544#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000545 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000546
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700547 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200548 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000549 return NULL;
550 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000551
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000552#ifdef MREMAP_MAYMOVE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000553 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000554#else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700555#if defined(__NetBSD__)
556 newmap = mremap(self->data, self->size, self->data, new_size, 0);
557#else
558 newmap = mremap(self->data, self->size, new_size, 0);
559#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000560#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000561 if (newmap == (void *)-1)
562 {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200563 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000564 return NULL;
565 }
566 self->data = newmap;
567 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200568 Py_RETURN_NONE;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000569#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000570#endif /* UNIX */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000571 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000572}
573
574static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000575mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000576{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000577 CHECK_VALID(NULL);
578 return PyLong_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000579}
580
581static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000582mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000583{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000584 Py_ssize_t offset = 0;
585 Py_ssize_t size = self->size;
586 CHECK_VALID(NULL);
587 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
588 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700589 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000590 PyErr_SetString(PyExc_ValueError, "flush values out of range");
591 return NULL;
592 }
R. David Murraye194dd62010-10-18 01:14:06 +0000593
594 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
595 return PyLong_FromLong(0);
596
Christian Heimesaf98da12008-01-27 15:18:18 +0000597#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000598 return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
Christian Heimesaf98da12008-01-27 15:18:18 +0000599#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000600 /* XXX semantics of return value? */
601 /* XXX flags for msync? */
602 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200603 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000604 return NULL;
605 }
606 return PyLong_FromLong(0);
Christian Heimesaf98da12008-01-27 15:18:18 +0000607#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000608 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
609 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000610#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000611}
612
613static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000614mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000615{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000616 Py_ssize_t dist;
617 int how=0;
618 CHECK_VALID(NULL);
619 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
620 return NULL;
621 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700622 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000623 switch (how) {
624 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000625 where = dist;
626 break;
627 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700628 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000629 goto onoutofrange;
630 where = self->pos + dist;
631 break;
632 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700633 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000634 goto onoutofrange;
635 where = self->size + dist;
636 break;
637 default:
638 PyErr_SetString(PyExc_ValueError, "unknown seek type");
639 return NULL;
640 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700641 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000642 goto onoutofrange;
643 self->pos = where;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200644 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000645 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000646
Tim Peters5ebfd362001-11-13 23:11:19 +0000647 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000648 PyErr_SetString(PyExc_ValueError, "seek out of range");
649 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000650}
651
652static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000653mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000654{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700655 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000656 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700657 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000658 !is_writable(self)) {
659 return NULL;
660 } else {
661 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700662 if (dest < 0 || src < 0 || cnt < 0)
663 goto bounds;
664 if (self->size - dest < cnt || self->size - src < cnt)
665 goto bounds;
666
667 memmove(&self->data[dest], &self->data[src], cnt);
668
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200669 Py_RETURN_NONE;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700670
671 bounds:
672 PyErr_SetString(PyExc_ValueError,
673 "source, destination, or count out of range");
674 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000675 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000676}
677
Georg Brandl0bccc182010-08-01 14:50:00 +0000678static PyObject *
679mmap_closed_get(mmap_object *self)
680{
681#ifdef MS_WINDOWS
682 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
683#elif defined(UNIX)
684 return PyBool_FromLong(self->data == NULL ? 1 : 0);
685#endif
686}
687
688static PyObject *
689mmap__enter__method(mmap_object *self, PyObject *args)
690{
691 CHECK_VALID(NULL);
692
693 Py_INCREF(self);
694 return (PyObject *)self;
695}
696
697static PyObject *
698mmap__exit__method(PyObject *self, PyObject *args)
699{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200700 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200701
702 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000703}
704
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300705#ifdef MS_WINDOWS
706static PyObject *
707mmap__sizeof__method(mmap_object *self, void *unused)
708{
709 Py_ssize_t res;
710
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200711 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300712 if (self->tagname)
713 res += strlen(self->tagname) + 1;
714 return PyLong_FromSsize_t(res);
715}
716#endif
717
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000718static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000719 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
720 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
721 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
722 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
723 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
724 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
725 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
726 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
727 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
728 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
729 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
730 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
731 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
732 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000733 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
734 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300735#ifdef MS_WINDOWS
736 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
737#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000738 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000739};
740
Georg Brandl0bccc182010-08-01 14:50:00 +0000741static PyGetSetDef mmap_object_getset[] = {
742 {"closed", (getter) mmap_closed_get, NULL, NULL},
743 {NULL}
744};
745
746
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000747/* Functions for treating an mmap'ed file as a buffer */
748
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000749static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000750mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000751{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000752 CHECK_VALID(-1);
753 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
754 (self->access == ACCESS_READ), flags) < 0)
755 return -1;
756 self->exports++;
757 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000758}
759
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000760static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000761mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000762{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000763 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000764}
765
Martin v. Löwis18e16552006-02-15 17:27:45 +0000766static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000767mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000768{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000769 CHECK_VALID(-1);
770 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000771}
772
773static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000774mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000775{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000776 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700777 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000778 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
779 return NULL;
780 }
781 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000782}
783
784static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000785mmap_subscript(mmap_object *self, PyObject *item)
786{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000787 CHECK_VALID(NULL);
788 if (PyIndex_Check(item)) {
789 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
790 if (i == -1 && PyErr_Occurred())
791 return NULL;
792 if (i < 0)
793 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700794 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000795 PyErr_SetString(PyExc_IndexError,
796 "mmap index out of range");
797 return NULL;
798 }
799 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
800 }
801 else if (PySlice_Check(item)) {
802 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000803
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000804 if (PySlice_GetIndicesEx(item, self->size,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000805 &start, &stop, &step, &slicelen) < 0) {
806 return NULL;
807 }
Guido van Rossum98297ee2007-11-06 21:34:58 +0000808
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000809 if (slicelen <= 0)
810 return PyBytes_FromStringAndSize("", 0);
811 else if (step == 1)
812 return PyBytes_FromStringAndSize(self->data + start,
813 slicelen);
814 else {
815 char *result_buf = (char *)PyMem_Malloc(slicelen);
816 Py_ssize_t cur, i;
817 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000818
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000819 if (result_buf == NULL)
820 return PyErr_NoMemory();
821 for (cur = start, i = 0; i < slicelen;
822 cur += step, i++) {
823 result_buf[i] = self->data[cur];
824 }
825 result = PyBytes_FromStringAndSize(result_buf,
826 slicelen);
827 PyMem_Free(result_buf);
828 return result;
829 }
830 }
831 else {
832 PyErr_SetString(PyExc_TypeError,
833 "mmap indices must be integers");
834 return NULL;
835 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000836}
837
838static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000839mmap_concat(mmap_object *self, PyObject *bb)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000840{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000841 CHECK_VALID(NULL);
842 PyErr_SetString(PyExc_SystemError,
843 "mmaps don't support concatenation");
844 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000845}
846
847static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000848mmap_repeat(mmap_object *self, Py_ssize_t n)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000849{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000850 CHECK_VALID(NULL);
851 PyErr_SetString(PyExc_SystemError,
852 "mmaps don't support repeat operation");
853 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000854}
855
856static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000857mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000858{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000859 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000860
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000861 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700862 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000863 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
864 return -1;
865 }
866 if (v == NULL) {
867 PyErr_SetString(PyExc_TypeError,
868 "mmap object doesn't support item deletion");
869 return -1;
870 }
871 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
872 PyErr_SetString(PyExc_IndexError,
873 "mmap assignment must be length-1 bytes()");
874 return -1;
875 }
876 if (!is_writable(self))
877 return -1;
878 buf = PyBytes_AsString(v);
879 self->data[i] = buf[0];
880 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000881}
882
Thomas Woutersed03b412007-08-28 21:37:11 +0000883static int
884mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
885{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000886 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000887
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000888 if (!is_writable(self))
889 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000890
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000891 if (PyIndex_Check(item)) {
892 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
893 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000894
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000895 if (i == -1 && PyErr_Occurred())
896 return -1;
897 if (i < 0)
898 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700899 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000900 PyErr_SetString(PyExc_IndexError,
901 "mmap index out of range");
902 return -1;
903 }
904 if (value == NULL) {
905 PyErr_SetString(PyExc_TypeError,
906 "mmap doesn't support item deletion");
907 return -1;
908 }
909 if (!PyIndex_Check(value)) {
910 PyErr_SetString(PyExc_TypeError,
911 "mmap item value must be an int");
912 return -1;
913 }
914 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
915 if (v == -1 && PyErr_Occurred())
916 return -1;
917 if (v < 0 || v > 255) {
918 PyErr_SetString(PyExc_ValueError,
919 "mmap item value must be "
920 "in range(0, 256)");
921 return -1;
922 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000923 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000924 return 0;
925 }
926 else if (PySlice_Check(item)) {
927 Py_ssize_t start, stop, step, slicelen;
928 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000929
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000930 if (PySlice_GetIndicesEx(item,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000931 self->size, &start, &stop,
932 &step, &slicelen) < 0) {
933 return -1;
934 }
935 if (value == NULL) {
936 PyErr_SetString(PyExc_TypeError,
937 "mmap object doesn't support slice deletion");
938 return -1;
939 }
940 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
941 return -1;
942 if (vbuf.len != slicelen) {
943 PyErr_SetString(PyExc_IndexError,
944 "mmap slice assignment is wrong size");
945 PyBuffer_Release(&vbuf);
946 return -1;
947 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000948
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000949 if (slicelen == 0) {
950 }
951 else if (step == 1) {
952 memcpy(self->data + start, vbuf.buf, slicelen);
953 }
954 else {
955 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000956
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000957 for (cur = start, i = 0;
958 i < slicelen;
959 cur += step, i++)
960 {
961 self->data[cur] = ((char *)vbuf.buf)[i];
962 }
963 }
964 PyBuffer_Release(&vbuf);
965 return 0;
966 }
967 else {
968 PyErr_SetString(PyExc_TypeError,
969 "mmap indices must be integer");
970 return -1;
971 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000972}
973
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000974static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100975 (lenfunc)mmap_length, /*sq_length*/
976 (binaryfunc)mmap_concat, /*sq_concat*/
977 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
978 (ssizeargfunc)mmap_item, /*sq_item*/
979 0, /*sq_slice*/
980 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
981 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000982};
983
Thomas Woutersed03b412007-08-28 21:37:11 +0000984static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000985 (lenfunc)mmap_length,
986 (binaryfunc)mmap_subscript,
987 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000988};
989
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000990static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000991 (getbufferproc)mmap_buffer_getbuf,
992 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000993};
994
Georg Brandl86def6c2008-01-21 20:36:10 +0000995static PyObject *
996new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
997
Christian Heimese1c98112008-01-21 11:20:28 +0000998PyDoc_STRVAR(mmap_doc,
999"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1000\n\
1001Maps length bytes from the file specified by the file handle fileno,\n\
1002and returns a mmap object. If length is larger than the current size\n\
1003of the file, the file is extended to contain length bytes. If length\n\
1004is 0, the maximum length of the map is the current size of the file,\n\
1005except that if the file is empty Windows raises an exception (you cannot\n\
1006create an empty mapping on Windows).\n\
1007\n\
1008Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1009\n\
1010Maps length bytes from the file specified by the file descriptor fileno,\n\
1011and returns a mmap object. If length is 0, the maximum length of the map\n\
1012will be the current size of the file when mmap is called.\n\
1013flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1014private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +00001015object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +00001016that's shared with all other processes mapping the same areas of the file.\n\
1017The default value is MAP_SHARED.\n\
1018\n\
1019To map anonymous memory, pass -1 as the fileno (both versions).");
1020
1021
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001022static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001023 PyVarObject_HEAD_INIT(NULL, 0)
1024 "mmap.mmap", /* tp_name */
1025 sizeof(mmap_object), /* tp_size */
1026 0, /* tp_itemsize */
1027 /* methods */
1028 (destructor) mmap_object_dealloc, /* tp_dealloc */
1029 0, /* tp_print */
1030 0, /* tp_getattr */
1031 0, /* tp_setattr */
1032 0, /* tp_reserved */
1033 0, /* tp_repr */
1034 0, /* tp_as_number */
1035 &mmap_as_sequence, /*tp_as_sequence*/
1036 &mmap_as_mapping, /*tp_as_mapping*/
1037 0, /*tp_hash*/
1038 0, /*tp_call*/
1039 0, /*tp_str*/
1040 PyObject_GenericGetAttr, /*tp_getattro*/
1041 0, /*tp_setattro*/
1042 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001043 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001044 mmap_doc, /*tp_doc*/
1045 0, /* tp_traverse */
1046 0, /* tp_clear */
1047 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001048 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001049 0, /* tp_iter */
1050 0, /* tp_iternext */
1051 mmap_object_methods, /* tp_methods */
1052 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001053 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001054 0, /* tp_base */
1055 0, /* tp_dict */
1056 0, /* tp_descr_get */
1057 0, /* tp_descr_set */
1058 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001059 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001060 PyType_GenericAlloc, /* tp_alloc */
1061 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001062 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001063};
1064
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001065
Tim Petersec0a5f02006-02-16 23:47:20 +00001066#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001067#ifdef HAVE_LARGEFILE_SUPPORT
1068#define _Py_PARSE_OFF_T "L"
1069#else
1070#define _Py_PARSE_OFF_T "l"
1071#endif
1072
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001073static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001074new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001075{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001076 struct _Py_stat_struct status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001077 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001078 Py_ssize_t map_size;
1079 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001080 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1081 int devzero = -1;
1082 int access = (int)ACCESS_DEFAULT;
1083 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001084 "flags", "prot",
1085 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001086
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001087 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1088 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001089 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001090 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001091 if (map_size < 0) {
1092 PyErr_SetString(PyExc_OverflowError,
1093 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001094 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001095 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001096 if (offset < 0) {
1097 PyErr_SetString(PyExc_OverflowError,
1098 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001099 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001100 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001101
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001102 if ((access != (int)ACCESS_DEFAULT) &&
1103 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1104 return PyErr_Format(PyExc_ValueError,
1105 "mmap can't specify both access and flags, prot.");
1106 switch ((access_mode)access) {
1107 case ACCESS_READ:
1108 flags = MAP_SHARED;
1109 prot = PROT_READ;
1110 break;
1111 case ACCESS_WRITE:
1112 flags = MAP_SHARED;
1113 prot = PROT_READ | PROT_WRITE;
1114 break;
1115 case ACCESS_COPY:
1116 flags = MAP_PRIVATE;
1117 prot = PROT_READ | PROT_WRITE;
1118 break;
1119 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001120 /* map prot to access type */
1121 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1122 /* ACCESS_DEFAULT */
1123 }
1124 else if (prot & PROT_WRITE) {
1125 access = ACCESS_WRITE;
1126 }
1127 else {
1128 access = ACCESS_READ;
1129 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001130 break;
1131 default:
1132 return PyErr_Format(PyExc_ValueError,
1133 "mmap invalid access parameter.");
1134 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001135
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001136#ifdef __APPLE__
1137 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1138 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1139 if (fd != -1)
1140 (void)fcntl(fd, F_FULLFSYNC);
1141#endif
Victor Stinnere134a7f2015-03-30 10:09:31 +02001142 if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1143 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001144 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001145 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001146 PyErr_SetString(PyExc_ValueError,
1147 "cannot mmap an empty file");
1148 return NULL;
1149 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001150 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001151 PyErr_SetString(PyExc_ValueError,
1152 "mmap offset is greater than file size");
1153 return NULL;
1154 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001155 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001156 PyErr_SetString(PyExc_ValueError,
1157 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001158 return NULL;
1159 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001160 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001161 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001162 PyErr_SetString(PyExc_ValueError,
1163 "mmap length is greater than file size");
1164 return NULL;
1165 }
1166 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001167 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1168 if (m_obj == NULL) {return NULL;}
1169 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001170 m_obj->size = map_size;
1171 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001172 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001173 m_obj->exports = 0;
1174 m_obj->offset = offset;
1175 if (fd == -1) {
1176 m_obj->fd = -1;
1177 /* Assume the caller wants to map anonymous memory.
1178 This is the same behaviour as Windows. mmap.mmap(-1, size)
1179 on both Windows and Unix map anonymous memory.
1180 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001181#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001182 /* BSD way to map anonymous memory */
1183 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001184#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001185 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001186 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001187 if (devzero == -1) {
1188 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001189 return NULL;
1190 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001191#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001192 }
1193 else {
1194 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001195 if (m_obj->fd == -1) {
1196 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001197 return NULL;
1198 }
1199 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001200
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001201 m_obj->data = mmap(NULL, map_size,
1202 prot, flags,
1203 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001204
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001205 if (devzero != -1) {
1206 close(devzero);
1207 }
1208
1209 if (m_obj->data == (char *)-1) {
1210 m_obj->data = NULL;
1211 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001212 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001213 return NULL;
1214 }
1215 m_obj->access = (access_mode)access;
1216 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001217}
1218#endif /* UNIX */
1219
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001220#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001221
1222/* A note on sizes and offsets: while the actual map size must hold in a
1223 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001224 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001225*/
1226
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001227static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001228new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001229{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001230 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001231 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001232 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001233 DWORD off_hi; /* upper 32 bits of offset */
1234 DWORD off_lo; /* lower 32 bits of offset */
1235 DWORD size_hi; /* upper 32 bits of size */
1236 DWORD size_lo; /* lower 32 bits of size */
1237 char *tagname = "";
1238 DWORD dwErr = 0;
1239 int fileno;
1240 HANDLE fh = 0;
1241 int access = (access_mode)ACCESS_DEFAULT;
1242 DWORD flProtect, dwDesiredAccess;
1243 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001244 "tagname",
1245 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001246
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001247 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1248 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001249 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001250 return NULL;
1251 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001252
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001253 switch((access_mode)access) {
1254 case ACCESS_READ:
1255 flProtect = PAGE_READONLY;
1256 dwDesiredAccess = FILE_MAP_READ;
1257 break;
1258 case ACCESS_DEFAULT: case ACCESS_WRITE:
1259 flProtect = PAGE_READWRITE;
1260 dwDesiredAccess = FILE_MAP_WRITE;
1261 break;
1262 case ACCESS_COPY:
1263 flProtect = PAGE_WRITECOPY;
1264 dwDesiredAccess = FILE_MAP_COPY;
1265 break;
1266 default:
1267 return PyErr_Format(PyExc_ValueError,
1268 "mmap invalid access parameter.");
1269 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001270
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001271 if (map_size < 0) {
1272 PyErr_SetString(PyExc_OverflowError,
1273 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001274 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001275 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001276 if (offset < 0) {
1277 PyErr_SetString(PyExc_OverflowError,
1278 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001279 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001280 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001281
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001282 /* assume -1 and 0 both mean invalid filedescriptor
1283 to 'anonymously' map memory.
1284 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1285 XXX: Should this code be added?
1286 if (fileno == 0)
1287 PyErr_WarnEx(PyExc_DeprecationWarning,
1288 "don't use 0 for anonymous memory",
1289 1);
1290 */
1291 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001292 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001293 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001294 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001295 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001296 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001297 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001298 return NULL;
1299 }
1300 /* Win9x appears to need us seeked to zero */
1301 lseek(fileno, 0, SEEK_SET);
1302 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001303
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001304 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1305 if (m_obj == NULL)
1306 return NULL;
1307 /* Set every field to an invalid marker, so we can safely
1308 destruct the object in the face of failure */
1309 m_obj->data = NULL;
1310 m_obj->file_handle = INVALID_HANDLE_VALUE;
1311 m_obj->map_handle = NULL;
1312 m_obj->tagname = NULL;
1313 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001314
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001315 if (fh) {
1316 /* It is necessary to duplicate the handle, so the
1317 Python code can close it on us */
1318 if (!DuplicateHandle(
1319 GetCurrentProcess(), /* source process handle */
1320 fh, /* handle to be duplicated */
1321 GetCurrentProcess(), /* target proc handle */
1322 (LPHANDLE)&m_obj->file_handle, /* result */
1323 0, /* access - ignored due to options value */
1324 FALSE, /* inherited by child processes? */
1325 DUPLICATE_SAME_ACCESS)) { /* options */
1326 dwErr = GetLastError();
1327 Py_DECREF(m_obj);
1328 PyErr_SetFromWindowsErr(dwErr);
1329 return NULL;
1330 }
1331 if (!map_size) {
1332 DWORD low,high;
1333 low = GetFileSize(fh, &high);
1334 /* low might just happen to have the value INVALID_FILE_SIZE;
1335 so we need to check the last error also. */
1336 if (low == INVALID_FILE_SIZE &&
1337 (dwErr = GetLastError()) != NO_ERROR) {
1338 Py_DECREF(m_obj);
1339 return PyErr_SetFromWindowsErr(dwErr);
1340 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001341
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001342 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001343 if (size == 0) {
1344 PyErr_SetString(PyExc_ValueError,
1345 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001346 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001347 return NULL;
1348 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001349 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001350 PyErr_SetString(PyExc_ValueError,
1351 "mmap offset is greater than file size");
1352 Py_DECREF(m_obj);
1353 return NULL;
1354 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001355 if (size - offset > PY_SSIZE_T_MAX) {
1356 PyErr_SetString(PyExc_ValueError,
1357 "mmap length is too large");
1358 Py_DECREF(m_obj);
1359 return NULL;
1360 }
1361 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001362 } else {
1363 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001364 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001365 }
1366 }
1367 else {
1368 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001369 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001370 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001371
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001372 /* set the initial position */
1373 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001374
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001375 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001376 m_obj->exports = 0;
1377 /* set the tag name */
1378 if (tagname != NULL && *tagname != '\0') {
1379 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1380 if (m_obj->tagname == NULL) {
1381 PyErr_NoMemory();
1382 Py_DECREF(m_obj);
1383 return NULL;
1384 }
1385 strcpy(m_obj->tagname, tagname);
1386 }
1387 else
1388 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001389
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001390 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001391 size_hi = (DWORD)(size >> 32);
1392 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001393 off_hi = (DWORD)(offset >> 32);
1394 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001395 /* For files, it would be sufficient to pass 0 as size.
1396 For anonymous maps, we have to pass the size explicitly. */
1397 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1398 NULL,
1399 flProtect,
1400 size_hi,
1401 size_lo,
1402 m_obj->tagname);
1403 if (m_obj->map_handle != NULL) {
1404 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1405 dwDesiredAccess,
1406 off_hi,
1407 off_lo,
1408 m_obj->size);
1409 if (m_obj->data != NULL)
1410 return (PyObject *)m_obj;
1411 else {
1412 dwErr = GetLastError();
1413 CloseHandle(m_obj->map_handle);
1414 m_obj->map_handle = NULL;
1415 }
1416 } else
1417 dwErr = GetLastError();
1418 Py_DECREF(m_obj);
1419 PyErr_SetFromWindowsErr(dwErr);
1420 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001421}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001422#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001423
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001424static void
1425setint(PyObject *d, const char *name, long value)
1426{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001427 PyObject *o = PyLong_FromLong(value);
1428 if (o && PyDict_SetItemString(d, name, o) == 0) {
1429 Py_DECREF(o);
1430 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001431}
1432
Martin v. Löwis1a214512008-06-11 05:26:20 +00001433
1434static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001435 PyModuleDef_HEAD_INIT,
1436 "mmap",
1437 NULL,
1438 -1,
1439 NULL,
1440 NULL,
1441 NULL,
1442 NULL,
1443 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001444};
1445
Mark Hammond62b1ab12002-07-23 06:31:15 +00001446PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001447PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001448{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001449 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001450
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001451 if (PyType_Ready(&mmap_object_type) < 0)
1452 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001453
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001454 module = PyModule_Create(&mmapmodule);
1455 if (module == NULL)
1456 return NULL;
1457 dict = PyModule_GetDict(module);
1458 if (!dict)
1459 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001460 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001461 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001462#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001463 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001464#endif
1465#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001466 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001467#endif
1468#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001469 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001470#endif
1471
1472#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001473 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001474#endif
1475#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001476 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001477#endif
1478#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001479 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001480#endif
1481#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001482 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001483#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001484#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001485 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1486 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001487#endif
1488
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001489 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001490
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001491 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001492
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001493 setint(dict, "ACCESS_READ", ACCESS_READ);
1494 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1495 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1496 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001497}