blob: 0dc480599f68ef7771a1b97bdb79d278b2988cc1 [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
Travis E. Oliphant8feafab2007-10-23 02:40:56 +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>
23
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000024#ifndef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000025#define UNIX
Benjamin Petersond7a4f9e2015-08-02 12:15:30 -070026# ifdef HAVE_FCNTL_H
Victor Stinner112d48a2011-05-03 14:36:36 +020027# include <fcntl.h>
Benjamin Petersond7a4f9e2015-08-02 12:15:30 -070028# endif /* HAVE_FCNTL_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000029#endif
30
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000031#ifdef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000032#include <windows.h>
Fred Drake145f96e2000-10-01 17:50:46 +000033static int
34my_getpagesize(void)
35{
Antoine Pitrouc83ea132010-05-09 14:46:46 +000036 SYSTEM_INFO si;
37 GetSystemInfo(&si);
38 return si.dwPageSize;
Fred Drake145f96e2000-10-01 17:50:46 +000039}
Travis E. Oliphant8feafab2007-10-23 02:40:56 +000040
41static int
42my_getallocationgranularity (void)
43{
44
Antoine Pitrouc83ea132010-05-09 14:46:46 +000045 SYSTEM_INFO si;
46 GetSystemInfo(&si);
47 return si.dwAllocationGranularity;
Travis E. Oliphant8feafab2007-10-23 02:40:56 +000048}
49
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000050#endif
51
52#ifdef UNIX
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000053#include <sys/mman.h>
Andrew M. Kuchling7b9fb922000-06-17 22:41:22 +000054#include <sys/stat.h>
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000055
Fred Drake145f96e2000-10-01 17:50:46 +000056#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
57static int
58my_getpagesize(void)
59{
Antoine Pitrouc83ea132010-05-09 14:46:46 +000060 return sysconf(_SC_PAGESIZE);
Fred Drake145f96e2000-10-01 17:50:46 +000061}
Travis E. Oliphant8feafab2007-10-23 02:40:56 +000062
63#define my_getallocationgranularity my_getpagesize
Fred Drake145f96e2000-10-01 17:50:46 +000064#else
65#define my_getpagesize getpagesize
66#endif
67
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000068#endif /* UNIX */
69
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000070#include <string.h>
Martin v. Löwis0e8bd7e2006-06-10 12:23:46 +000071
72#ifdef HAVE_SYS_TYPES_H
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000073#include <sys/types.h>
Martin v. Löwis0e8bd7e2006-06-10 12:23:46 +000074#endif /* HAVE_SYS_TYPES_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000075
Neal Norwitz3eaf2b52006-02-16 08:08:54 +000076/* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +000077#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
78# define MAP_ANONYMOUS MAP_ANON
79#endif
80
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000081static PyObject *mmap_module_error;
82
Tim Peters5ebfd362001-11-13 23:11:19 +000083typedef enum
84{
Antoine Pitrouc83ea132010-05-09 14:46:46 +000085 ACCESS_DEFAULT,
86 ACCESS_READ,
87 ACCESS_WRITE,
88 ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +000089} access_mode;
90
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000091typedef struct {
Antoine Pitrouc83ea132010-05-09 14:46:46 +000092 PyObject_HEAD
93 char * data;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -070094 Py_ssize_t size;
95 Py_ssize_t pos; /* relative to offset */
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +000096#ifdef MS_WINDOWS
97 PY_LONG_LONG offset;
98#else
99 off_t offset;
100#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000101
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000102#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +0000109 int fd;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000110#endif
Tim Peters5ebfd362001-11-13 23:11:19 +0000111
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000112 access_mode access;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000113} mmap_object;
114
Tim Peters5ebfd362001-11-13 23:11:19 +0000115
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000116static void
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000117mmap_object_dealloc(mmap_object *m_obj)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000118{
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000119#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000120 if (m_obj->data != NULL)
121 UnmapViewOfFile (m_obj->data);
122 if (m_obj->map_handle != NULL)
123 CloseHandle (m_obj->map_handle);
124 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
125 CloseHandle (m_obj->file_handle);
126 if (m_obj->tagname)
127 PyMem_Free(m_obj->tagname);
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000128#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000129
130#ifdef UNIX
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000131 if (m_obj->fd >= 0)
132 (void) close(m_obj->fd);
133 if (m_obj->data!=NULL) {
R. David Murray2a4b3fc2010-12-11 02:13:04 +0000134 if (m_obj->access != ACCESS_READ && m_obj->access != ACCESS_COPY)
135 msync(m_obj->data, m_obj->size, MS_SYNC);
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000136 munmap(m_obj->data, m_obj->size);
137 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000138#endif /* UNIX */
139
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000140 Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000141}
142
143static PyObject *
Georg Brandl96a8c392006-05-29 21:04:52 +0000144mmap_close_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000145{
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000146#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000147 /* For each resource we maintain, we need to check
148 the value is valid, and if so, free the resource
149 and set the member value to an invalid value so
150 the dealloc does not attempt to resource clearing
151 again.
152 TODO - should we check for errors in the close operations???
153 */
154 if (self->data != NULL) {
155 UnmapViewOfFile(self->data);
156 self->data = NULL;
157 }
158 if (self->map_handle != NULL) {
159 CloseHandle(self->map_handle);
160 self->map_handle = NULL;
161 }
162 if (self->file_handle != INVALID_HANDLE_VALUE) {
163 CloseHandle(self->file_handle);
164 self->file_handle = INVALID_HANDLE_VALUE;
165 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000166#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000167
168#ifdef UNIX
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000169 if (0 <= self->fd)
170 (void) close(self->fd);
171 self->fd = -1;
172 if (self->data != NULL) {
173 munmap(self->data, self->size);
174 self->data = NULL;
175 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000176#endif
177
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000178 Py_INCREF(Py_None);
179 return Py_None;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000180}
181
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000182#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000183#define CHECK_VALID(err) \
184do { \
185 if (self->map_handle == NULL) { \
186 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
187 return err; \
188 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000189} while (0)
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000190#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000191
192#ifdef UNIX
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000193#define CHECK_VALID(err) \
194do { \
195 if (self->data == NULL) { \
196 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
197 return err; \
198 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000199} while (0)
200#endif /* UNIX */
201
202static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000203mmap_read_byte_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000204 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000205{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000206 CHECK_VALID(NULL);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700207 if (self->pos >= self->size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000208 PyErr_SetString(PyExc_ValueError, "read byte out of range");
209 return NULL;
210 }
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700211 return PyString_FromStringAndSize(&self->data[self->pos++], 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000212}
213
214static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000215mmap_read_line_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000216 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000217{
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700218 Py_ssize_t remaining;
219 char *start, *eol;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000220 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000221
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000222 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000223
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700224 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
225 if (!remaining)
226 return PyString_FromString("");
227 start = self->data + self->pos;
228 eol = memchr(start, '\n', remaining);
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000229 if (!eol)
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700230 eol = self->data + self->size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000231 else
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700232 ++eol; /* advance past newline */
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000233 result = PyString_FromStringAndSize(start, (eol - start));
234 self->pos += (eol - start);
235 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000236}
237
238static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000239mmap_read_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000240 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000241{
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700242 Py_ssize_t num_bytes, remaining;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000243 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000244
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000245 CHECK_VALID(NULL);
246 if (!PyArg_ParseTuple(args, "n:read", &num_bytes))
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700247 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000248
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000249 /* silently 'adjust' out-of-range requests */
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700250 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
251 if (num_bytes < 0 || num_bytes > remaining)
252 num_bytes = remaining;
253 result = PyString_FromStringAndSize(&self->data[self->pos], num_bytes);
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000254 self->pos += num_bytes;
255 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000256}
257
258static PyObject *
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000259mmap_gfind(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000260 PyObject *args,
261 int reverse)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000262{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000263 Py_ssize_t start = self->pos;
264 Py_ssize_t end = self->size;
265 const char *needle;
266 Py_ssize_t len;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000267
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000268 CHECK_VALID(NULL);
269 if (!PyArg_ParseTuple(args, reverse ? "s#|nn:rfind" : "s#|nn:find",
270 &needle, &len, &start, &end)) {
271 return NULL;
272 } else {
273 const char *p, *start_p, *end_p;
274 int sign = reverse ? -1 : 1;
Greg Stein834f4dd2001-05-14 09:32:26 +0000275
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000276 if (start < 0)
277 start += self->size;
278 if (start < 0)
279 start = 0;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700280 else if (start > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000281 start = self->size;
Greg Stein834f4dd2001-05-14 09:32:26 +0000282
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000283 if (end < 0)
284 end += self->size;
285 if (end < 0)
286 end = 0;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700287 else if (end > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000288 end = self->size;
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000289
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000290 start_p = self->data + start;
291 end_p = self->data + end;
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000292
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000293 for (p = (reverse ? end_p - len : start_p);
294 (p >= start_p) && (p + len <= end_p); p += sign) {
295 Py_ssize_t i;
296 for (i = 0; i < len && needle[i] == p[i]; ++i)
297 /* nothing */;
298 if (i == len) {
299 return PyInt_FromSsize_t(p - self->data);
300 }
301 }
302 return PyInt_FromLong(-1);
303 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000304}
305
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000306static PyObject *
307mmap_find_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000308 PyObject *args)
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000309{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000310 return mmap_gfind(self, args, 0);
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000311}
312
313static PyObject *
314mmap_rfind_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000315 PyObject *args)
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000316{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000317 return mmap_gfind(self, args, 1);
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000318}
319
Tim Petersec0a5f02006-02-16 23:47:20 +0000320static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000321is_writeable(mmap_object *self)
322{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000323 if (self->access != ACCESS_READ)
324 return 1;
325 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
326 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000327}
328
Tim Petersec0a5f02006-02-16 23:47:20 +0000329static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000330is_resizeable(mmap_object *self)
331{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000332 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
333 return 1;
334 PyErr_Format(PyExc_TypeError,
335 "mmap can't resize a readonly or copy-on-write memory map.");
336 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000337}
338
339
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000340static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000341mmap_write_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000342 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000343{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000344 Py_ssize_t length;
345 char *data;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000346
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000347 CHECK_VALID(NULL);
348 if (!PyArg_ParseTuple(args, "s#:write", &data, &length))
349 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000350
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000351 if (!is_writeable(self))
352 return NULL;
Tim Peters5ebfd362001-11-13 23:11:19 +0000353
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700354 if (self->pos > self->size || self->size - self->pos < length) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000355 PyErr_SetString(PyExc_ValueError, "data out of range");
356 return NULL;
357 }
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700358 memcpy(&self->data[self->pos], data, length);
359 self->pos += length;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000360 Py_INCREF(Py_None);
361 return Py_None;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000362}
363
364static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000365mmap_write_byte_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000366 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000367{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000368 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000369
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000370 CHECK_VALID(NULL);
371 if (!PyArg_ParseTuple(args, "c:write_byte", &value))
372 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000373
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000374 if (!is_writeable(self))
375 return NULL;
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000376
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000377 if (self->pos < self->size) {
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700378 self->data[self->pos++] = value;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000379 Py_INCREF(Py_None);
380 return Py_None;
381 }
382 else {
383 PyErr_SetString(PyExc_ValueError, "write byte out of range");
384 return NULL;
385 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000386}
Tim Petersec0a5f02006-02-16 23:47:20 +0000387
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000388static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000389mmap_size_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000390 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000391{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000392 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000393
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000394#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000395 if (self->file_handle != INVALID_HANDLE_VALUE) {
396 DWORD low,high;
397 PY_LONG_LONG size;
398 low = GetFileSize(self->file_handle, &high);
399 if (low == INVALID_FILE_SIZE) {
400 /* It might be that the function appears to have failed,
401 when indeed its size equals INVALID_FILE_SIZE */
402 DWORD error = GetLastError();
403 if (error != NO_ERROR)
404 return PyErr_SetFromWindowsErr(error);
405 }
406 if (!high && low < LONG_MAX)
407 return PyInt_FromLong((long)low);
408 size = (((PY_LONG_LONG)high)<<32) + low;
409 return PyLong_FromLongLong(size);
410 } else {
411 return PyInt_FromSsize_t(self->size);
412 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000413#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000414
415#ifdef UNIX
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000416 {
417 struct stat buf;
418 if (-1 == fstat(self->fd, &buf)) {
419 PyErr_SetFromErrno(mmap_module_error);
420 return NULL;
421 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000422#ifdef HAVE_LARGEFILE_SUPPORT
423 return PyLong_FromLongLong(buf.st_size);
424#else
425 return PyInt_FromLong(buf.st_size);
426#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000427 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000428#endif /* UNIX */
429}
430
431/* This assumes that you want the entire file mapped,
432 / and when recreating the map will make the new file
433 / have the new size
434 /
435 / Is this really necessary? This could easily be done
436 / from python by just closing and re-opening with the
437 / new size?
438 */
439
440static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000441mmap_resize_method(mmap_object *self,
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000442 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000443{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000444 Py_ssize_t new_size;
445 CHECK_VALID(NULL);
446 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
447 !is_resizeable(self)) {
448 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700449 }
450 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
451 PyErr_SetString(PyExc_ValueError, "new size out of range");
452 return NULL;
453 }
454
455 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000456#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000457 DWORD dwErrCode = 0;
458 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
459 /* First, unmap the file view */
460 UnmapViewOfFile(self->data);
461 self->data = NULL;
462 /* Close the mapping object */
463 CloseHandle(self->map_handle);
464 self->map_handle = NULL;
465 /* Move to the desired EOF position */
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000466 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
467 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
468 off_hi = (DWORD)(self->offset >> 32);
469 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000470 SetFilePointer(self->file_handle,
471 newSizeLow, &newSizeHigh, FILE_BEGIN);
472 /* Change the size of the file */
473 SetEndOfFile(self->file_handle);
474 /* Create another mapping object and remap the file view */
475 self->map_handle = CreateFileMapping(
476 self->file_handle,
477 NULL,
478 PAGE_READWRITE,
479 0,
480 0,
481 self->tagname);
482 if (self->map_handle != NULL) {
483 self->data = (char *) MapViewOfFile(self->map_handle,
484 FILE_MAP_WRITE,
485 off_hi,
486 off_lo,
487 new_size);
488 if (self->data != NULL) {
489 self->size = new_size;
490 Py_INCREF(Py_None);
491 return Py_None;
492 } else {
493 dwErrCode = GetLastError();
494 CloseHandle(self->map_handle);
495 self->map_handle = NULL;
496 }
497 } else {
498 dwErrCode = GetLastError();
499 }
500 PyErr_SetFromWindowsErr(dwErrCode);
501 return NULL;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000502#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000503
504#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000505#ifndef HAVE_MREMAP
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000506 PyErr_SetString(PyExc_SystemError,
507 "mmap: resizing not available--no mremap()");
508 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000509#else
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000510 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000511
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700512 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000513 PyErr_SetFromErrno(mmap_module_error);
514 return NULL;
515 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000516
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000517#ifdef MREMAP_MAYMOVE
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000518 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000519#else
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700520#if defined(__NetBSD__)
521 newmap = mremap(self->data, self->size, self->data, new_size, 0);
522#else
523 newmap = mremap(self->data, self->size, new_size, 0);
524#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000525#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000526 if (newmap == (void *)-1)
527 {
528 PyErr_SetFromErrno(mmap_module_error);
529 return NULL;
530 }
531 self->data = newmap;
532 self->size = new_size;
533 Py_INCREF(Py_None);
534 return Py_None;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000535#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000536#endif /* UNIX */
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000537 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000538}
539
540static PyObject *
Georg Brandl96a8c392006-05-29 21:04:52 +0000541mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000542{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000543 CHECK_VALID(NULL);
544 return PyInt_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000545}
546
547static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000548mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000549{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000550 Py_ssize_t offset = 0;
551 Py_ssize_t size = self->size;
552 CHECK_VALID(NULL);
553 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
554 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700555 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000556 PyErr_SetString(PyExc_ValueError, "flush values out of range");
557 return NULL;
558 }
R. David Murray2a4b3fc2010-12-11 02:13:04 +0000559
560 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
561 return PyLong_FromLong(0);
562
Neal Norwitz448654f2008-01-27 07:36:03 +0000563#ifdef MS_WINDOWS
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000564 return PyInt_FromLong((long) FlushViewOfFile(self->data+offset, size));
Neal Norwitz448654f2008-01-27 07:36:03 +0000565#elif defined(UNIX)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000566 /* XXX semantics of return value? */
567 /* XXX flags for msync? */
568 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
569 PyErr_SetFromErrno(mmap_module_error);
570 return NULL;
571 }
572 return PyInt_FromLong(0);
Neal Norwitz448654f2008-01-27 07:36:03 +0000573#else
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000574 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
575 return NULL;
Neal Norwitz448654f2008-01-27 07:36:03 +0000576#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000577}
578
579static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000580mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000581{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000582 Py_ssize_t dist;
583 int how=0;
584 CHECK_VALID(NULL);
585 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
586 return NULL;
587 else {
Benjamin Petersonbc4bdbd2016-10-05 23:26:24 -0700588 Py_ssize_t where;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000589 switch (how) {
590 case 0: /* relative to start */
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000591 where = dist;
592 break;
593 case 1: /* relative to current position */
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700594 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000595 goto onoutofrange;
596 where = self->pos + dist;
597 break;
598 case 2: /* relative to end */
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700599 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000600 goto onoutofrange;
601 where = self->size + dist;
602 break;
603 default:
604 PyErr_SetString(PyExc_ValueError, "unknown seek type");
605 return NULL;
606 }
Benjamin Petersonbc4bdbd2016-10-05 23:26:24 -0700607 if (where > self->size || where < 0)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000608 goto onoutofrange;
609 self->pos = where;
610 Py_INCREF(Py_None);
611 return Py_None;
612 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000613
Tim Peters5ebfd362001-11-13 23:11:19 +0000614 onoutofrange:
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000615 PyErr_SetString(PyExc_ValueError, "seek out of range");
616 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000617}
618
619static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000620mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000621{
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700622 Py_ssize_t dest, src, cnt;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000623 CHECK_VALID(NULL);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700624 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000625 !is_writeable(self)) {
626 return NULL;
627 } else {
628 /* bounds check the values */
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700629 if (dest < 0 || src < 0 || cnt < 0)
630 goto bounds;
631 if (self->size - dest < cnt || self->size - src < cnt)
632 goto bounds;
633
634 memmove(&self->data[dest], &self->data[src], cnt);
635
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000636 Py_INCREF(Py_None);
637 return Py_None;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700638
639 bounds:
640 PyErr_SetString(PyExc_ValueError,
641 "source, destination, or count out of range");
642 return NULL;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000643 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000644}
645
Serhiy Storchakacbee9722014-08-19 17:03:42 +0300646#ifdef MS_WINDOWS
647static PyObject *
648mmap__sizeof__method(mmap_object *self, void *unused)
649{
650 Py_ssize_t res;
651
Serhiy Storchakac06a6d02015-12-19 20:07:48 +0200652 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchakacbee9722014-08-19 17:03:42 +0300653 if (self->tagname)
654 res += strlen(self->tagname) + 1;
655 return PyLong_FromSsize_t(res);
656}
657#endif
658
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000659static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000660 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
661 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
662 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
663 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
664 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
665 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
666 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
667 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
668 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
669 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
670 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
671 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
672 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
673 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Serhiy Storchakacbee9722014-08-19 17:03:42 +0300674#ifdef MS_WINDOWS
675 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
676#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000677 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000678};
679
680/* Functions for treating an mmap'ed file as a buffer */
681
Martin v. Löwis18e16552006-02-15 17:27:45 +0000682static Py_ssize_t
683mmap_buffer_getreadbuf(mmap_object *self, Py_ssize_t index, const void **ptr)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000684{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000685 CHECK_VALID(-1);
686 if (index != 0) {
687 PyErr_SetString(PyExc_SystemError,
688 "Accessing non-existent mmap segment");
689 return -1;
690 }
691 *ptr = self->data;
692 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000693}
694
Martin v. Löwis18e16552006-02-15 17:27:45 +0000695static Py_ssize_t
696mmap_buffer_getwritebuf(mmap_object *self, Py_ssize_t index, const void **ptr)
Tim Petersec0a5f02006-02-16 23:47:20 +0000697{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000698 CHECK_VALID(-1);
699 if (index != 0) {
700 PyErr_SetString(PyExc_SystemError,
701 "Accessing non-existent mmap segment");
702 return -1;
703 }
704 if (!is_writeable(self))
705 return -1;
706 *ptr = self->data;
707 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000708}
709
Martin v. Löwis18e16552006-02-15 17:27:45 +0000710static Py_ssize_t
711mmap_buffer_getsegcount(mmap_object *self, Py_ssize_t *lenp)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000712{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000713 CHECK_VALID(-1);
714 if (lenp)
715 *lenp = self->size;
716 return 1;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000717}
718
Martin v. Löwis18e16552006-02-15 17:27:45 +0000719static Py_ssize_t
720mmap_buffer_getcharbuffer(mmap_object *self, Py_ssize_t index, const void **ptr)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000721{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000722 if (index != 0) {
723 PyErr_SetString(PyExc_SystemError,
724 "accessing non-existent buffer segment");
725 return -1;
726 }
727 *ptr = (const char *)self->data;
728 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000729}
730
Martin v. Löwis18e16552006-02-15 17:27:45 +0000731static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000732mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000733{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000734 CHECK_VALID(-1);
735 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000736}
737
738static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000739mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000740{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000741 CHECK_VALID(NULL);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700742 if (i < 0 || i >= self->size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000743 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
744 return NULL;
745 }
746 return PyString_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000747}
748
749static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000750mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000751{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000752 CHECK_VALID(NULL);
753 if (ilow < 0)
754 ilow = 0;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700755 else if (ilow > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000756 ilow = self->size;
757 if (ihigh < 0)
758 ihigh = 0;
759 if (ihigh < ilow)
760 ihigh = ilow;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700761 else if (ihigh > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000762 ihigh = self->size;
Tim Petersec0a5f02006-02-16 23:47:20 +0000763
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000764 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000765}
766
767static PyObject *
Thomas Wouters3ccec682007-08-28 15:28:19 +0000768mmap_subscript(mmap_object *self, PyObject *item)
769{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000770 CHECK_VALID(NULL);
771 if (PyIndex_Check(item)) {
772 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
773 if (i == -1 && PyErr_Occurred())
774 return NULL;
775 if (i < 0)
776 i += self->size;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700777 if (i < 0 || i >= self->size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000778 PyErr_SetString(PyExc_IndexError,
779 "mmap index out of range");
780 return NULL;
781 }
782 return PyString_FromStringAndSize(self->data + i, 1);
783 }
784 else if (PySlice_Check(item)) {
785 Py_ssize_t start, stop, step, slicelen;
Thomas Wouters3ccec682007-08-28 15:28:19 +0000786
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000787 if (PySlice_GetIndicesEx((PySliceObject *)item, self->size,
788 &start, &stop, &step, &slicelen) < 0) {
789 return NULL;
790 }
Thomas Wouters3ccec682007-08-28 15:28:19 +0000791
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000792 if (slicelen <= 0)
793 return PyString_FromStringAndSize("", 0);
794 else if (step == 1)
795 return PyString_FromStringAndSize(self->data + start,
796 slicelen);
797 else {
798 char *result_buf = (char *)PyMem_Malloc(slicelen);
799 Py_ssize_t cur, i;
800 PyObject *result;
801
802 if (result_buf == NULL)
803 return PyErr_NoMemory();
804 for (cur = start, i = 0; i < slicelen;
805 cur += step, i++) {
806 result_buf[i] = self->data[cur];
807 }
808 result = PyString_FromStringAndSize(result_buf,
809 slicelen);
810 PyMem_Free(result_buf);
811 return result;
812 }
813 }
814 else {
815 PyErr_SetString(PyExc_TypeError,
816 "mmap indices must be integers");
817 return NULL;
818 }
Thomas Wouters3ccec682007-08-28 15:28:19 +0000819}
820
821static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000822mmap_concat(mmap_object *self, PyObject *bb)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000823{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000824 CHECK_VALID(NULL);
825 PyErr_SetString(PyExc_SystemError,
826 "mmaps don't support concatenation");
827 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000828}
829
830static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000831mmap_repeat(mmap_object *self, Py_ssize_t n)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000832{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000833 CHECK_VALID(NULL);
834 PyErr_SetString(PyExc_SystemError,
835 "mmaps don't support repeat operation");
836 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000837}
838
839static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000840mmap_ass_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000841{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000842 const char *buf;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000843
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000844 CHECK_VALID(-1);
845 if (ilow < 0)
846 ilow = 0;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700847 else if (ilow > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000848 ilow = self->size;
849 if (ihigh < 0)
850 ihigh = 0;
851 if (ihigh < ilow)
852 ihigh = ilow;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700853 else if (ihigh > self->size)
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000854 ihigh = self->size;
Tim Petersec0a5f02006-02-16 23:47:20 +0000855
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000856 if (v == NULL) {
857 PyErr_SetString(PyExc_TypeError,
858 "mmap object doesn't support slice deletion");
859 return -1;
860 }
861 if (! (PyString_Check(v)) ) {
862 PyErr_SetString(PyExc_IndexError,
863 "mmap slice assignment must be a string");
864 return -1;
865 }
866 if (PyString_Size(v) != (ihigh - ilow)) {
867 PyErr_SetString(PyExc_IndexError,
868 "mmap slice assignment is wrong size");
869 return -1;
870 }
871 if (!is_writeable(self))
872 return -1;
873 buf = PyString_AsString(v);
874 memcpy(self->data + ilow, buf, ihigh-ilow);
875 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000876}
877
878static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000879mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000880{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000881 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000882
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000883 CHECK_VALID(-1);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700884 if (i < 0 || i >= self->size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000885 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
886 return -1;
887 }
888 if (v == NULL) {
889 PyErr_SetString(PyExc_TypeError,
890 "mmap object doesn't support item deletion");
891 return -1;
892 }
893 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
894 PyErr_SetString(PyExc_IndexError,
895 "mmap assignment must be single-character string");
896 return -1;
897 }
898 if (!is_writeable(self))
899 return -1;
900 buf = PyString_AsString(v);
901 self->data[i] = buf[0];
902 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000903}
904
Thomas Wouters3ccec682007-08-28 15:28:19 +0000905static int
906mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
907{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000908 CHECK_VALID(-1);
Thomas Wouters3ccec682007-08-28 15:28:19 +0000909
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000910 if (PyIndex_Check(item)) {
911 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
912 const char *buf;
Thomas Wouters3ccec682007-08-28 15:28:19 +0000913
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000914 if (i == -1 && PyErr_Occurred())
915 return -1;
916 if (i < 0)
917 i += self->size;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -0700918 if (i < 0 || i >= self->size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000919 PyErr_SetString(PyExc_IndexError,
920 "mmap index out of range");
921 return -1;
922 }
923 if (value == NULL) {
924 PyErr_SetString(PyExc_TypeError,
925 "mmap object doesn't support item deletion");
926 return -1;
927 }
928 if (!PyString_Check(value) || PyString_Size(value) != 1) {
929 PyErr_SetString(PyExc_IndexError,
930 "mmap assignment must be single-character string");
931 return -1;
932 }
933 if (!is_writeable(self))
934 return -1;
935 buf = PyString_AsString(value);
936 self->data[i] = buf[0];
937 return 0;
938 }
939 else if (PySlice_Check(item)) {
940 Py_ssize_t start, stop, step, slicelen;
Thomas Wouters3ccec682007-08-28 15:28:19 +0000941
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000942 if (PySlice_GetIndicesEx((PySliceObject *)item,
943 self->size, &start, &stop,
944 &step, &slicelen) < 0) {
945 return -1;
946 }
947 if (value == NULL) {
948 PyErr_SetString(PyExc_TypeError,
949 "mmap object doesn't support slice deletion");
950 return -1;
951 }
952 if (!PyString_Check(value)) {
953 PyErr_SetString(PyExc_IndexError,
954 "mmap slice assignment must be a string");
955 return -1;
956 }
957 if (PyString_Size(value) != slicelen) {
958 PyErr_SetString(PyExc_IndexError,
959 "mmap slice assignment is wrong size");
960 return -1;
961 }
962 if (!is_writeable(self))
963 return -1;
Thomas Wouters3ccec682007-08-28 15:28:19 +0000964
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000965 if (slicelen == 0)
966 return 0;
967 else if (step == 1) {
968 const char *buf = PyString_AsString(value);
969
970 if (buf == NULL)
971 return -1;
972 memcpy(self->data + start, buf, slicelen);
973 return 0;
974 }
975 else {
976 Py_ssize_t cur, i;
977 const char *buf = PyString_AsString(value);
978
979 if (buf == NULL)
980 return -1;
981 for (cur = start, i = 0; i < slicelen;
982 cur += step, i++) {
983 self->data[cur] = buf[i];
984 }
985 return 0;
986 }
987 }
988 else {
989 PyErr_SetString(PyExc_TypeError,
990 "mmap indices must be integer");
991 return -1;
992 }
Thomas Wouters3ccec682007-08-28 15:28:19 +0000993}
994
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000995static PySequenceMethods mmap_as_sequence = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000996 (lenfunc)mmap_length, /*sq_length*/
997 (binaryfunc)mmap_concat, /*sq_concat*/
998 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
999 (ssizeargfunc)mmap_item, /*sq_item*/
1000 (ssizessizeargfunc)mmap_slice, /*sq_slice*/
1001 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
1002 (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001003};
1004
Thomas Wouters3ccec682007-08-28 15:28:19 +00001005static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001006 (lenfunc)mmap_length,
1007 (binaryfunc)mmap_subscript,
1008 (objobjargproc)mmap_ass_subscript,
Thomas Wouters3ccec682007-08-28 15:28:19 +00001009};
1010
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001011static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001012 (readbufferproc)mmap_buffer_getreadbuf,
1013 (writebufferproc)mmap_buffer_getwritebuf,
1014 (segcountproc)mmap_buffer_getsegcount,
1015 (charbufferproc)mmap_buffer_getcharbuffer,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001016};
1017
Georg Brandl845c4032008-01-21 14:16:46 +00001018static PyObject *
1019new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1020
Georg Brandlef928022008-01-20 14:50:05 +00001021PyDoc_STRVAR(mmap_doc,
1022"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1023\n\
1024Maps length bytes from the file specified by the file handle fileno,\n\
1025and returns a mmap object. If length is larger than the current size\n\
1026of the file, the file is extended to contain length bytes. If length\n\
1027is 0, the maximum length of the map is the current size of the file,\n\
1028except that if the file is empty Windows raises an exception (you cannot\n\
1029create an empty mapping on Windows).\n\
1030\n\
1031Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1032\n\
1033Maps length bytes from the file specified by the file descriptor fileno,\n\
1034and returns a mmap object. If length is 0, the maximum length of the map\n\
1035will be the current size of the file when mmap is called.\n\
1036flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1037private copy-on-write mapping, so changes to the contents of the mmap\n\
Thomas Heller66260992008-08-19 17:47:13 +00001038object will be private to this process, and MAP_SHARED creates a mapping\n\
Georg Brandlef928022008-01-20 14:50:05 +00001039that's shared with all other processes mapping the same areas of the file.\n\
1040The default value is MAP_SHARED.\n\
1041\n\
1042To map anonymous memory, pass -1 as the fileno (both versions).");
1043
1044
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001045static PyTypeObject mmap_object_type = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001046 PyVarObject_HEAD_INIT(NULL, 0)
1047 "mmap.mmap", /* tp_name */
1048 sizeof(mmap_object), /* tp_size */
1049 0, /* tp_itemsize */
1050 /* methods */
1051 (destructor) mmap_object_dealloc, /* tp_dealloc */
1052 0, /* tp_print */
1053 0, /* tp_getattr */
1054 0, /* tp_setattr */
1055 0, /* tp_compare */
1056 0, /* tp_repr */
1057 0, /* tp_as_number */
1058 &mmap_as_sequence, /*tp_as_sequence*/
1059 &mmap_as_mapping, /*tp_as_mapping*/
1060 0, /*tp_hash*/
1061 0, /*tp_call*/
1062 0, /*tp_str*/
1063 PyObject_GenericGetAttr, /*tp_getattro*/
1064 0, /*tp_setattro*/
1065 &mmap_as_buffer, /*tp_as_buffer*/
1066 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
1067 mmap_doc, /*tp_doc*/
1068 0, /* tp_traverse */
1069 0, /* tp_clear */
1070 0, /* tp_richcompare */
1071 0, /* tp_weaklistoffset */
1072 0, /* tp_iter */
1073 0, /* tp_iternext */
1074 mmap_object_methods, /* tp_methods */
1075 0, /* tp_members */
1076 0, /* tp_getset */
1077 0, /* tp_base */
1078 0, /* tp_dict */
1079 0, /* tp_descr_get */
1080 0, /* tp_descr_set */
1081 0, /* tp_dictoffset */
1082 0, /* tp_init */
1083 PyType_GenericAlloc, /* tp_alloc */
1084 new_mmap_object, /* tp_new */
1085 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001086};
1087
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001088
Tim Petersec0a5f02006-02-16 23:47:20 +00001089#ifdef UNIX
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001090#ifdef HAVE_LARGEFILE_SUPPORT
1091#define _Py_PARSE_OFF_T "L"
1092#else
1093#define _Py_PARSE_OFF_T "l"
1094#endif
1095
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001096static PyObject *
Georg Brandl845c4032008-01-21 14:16:46 +00001097new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001098{
Neal Norwitzb5673922002-09-05 21:48:07 +00001099#ifdef HAVE_FSTAT
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001100 struct stat st;
Neal Norwitzb5673922002-09-05 21:48:07 +00001101#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001102 mmap_object *m_obj;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001103 Py_ssize_t map_size;
1104 off_t offset = 0;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001105 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1106 int devzero = -1;
1107 int access = (int)ACCESS_DEFAULT;
1108 static char *keywords[] = {"fileno", "length",
1109 "flags", "prot",
1110 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001111
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001112 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1113 &fd, &map_size, &flags, &prot,
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001114 &access, &offset))
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001115 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001116 if (map_size < 0) {
1117 PyErr_SetString(PyExc_OverflowError,
1118 "memory mapped length must be postiive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001119 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001120 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001121 if (offset < 0) {
1122 PyErr_SetString(PyExc_OverflowError,
1123 "memory mapped offset must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001124 return NULL;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001125 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001126
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001127 if ((access != (int)ACCESS_DEFAULT) &&
1128 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1129 return PyErr_Format(PyExc_ValueError,
1130 "mmap can't specify both access and flags, prot.");
1131 switch ((access_mode)access) {
1132 case ACCESS_READ:
1133 flags = MAP_SHARED;
1134 prot = PROT_READ;
1135 break;
1136 case ACCESS_WRITE:
1137 flags = MAP_SHARED;
1138 prot = PROT_READ | PROT_WRITE;
1139 break;
1140 case ACCESS_COPY:
1141 flags = MAP_PRIVATE;
1142 prot = PROT_READ | PROT_WRITE;
1143 break;
1144 case ACCESS_DEFAULT:
Antoine Pitroud6f3a3e2011-03-06 02:03:34 +01001145 /* map prot to access type */
1146 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1147 /* ACCESS_DEFAULT */
1148 }
1149 else if (prot & PROT_WRITE) {
1150 access = ACCESS_WRITE;
1151 }
1152 else {
1153 access = ACCESS_READ;
1154 }
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001155 break;
1156 default:
1157 return PyErr_Format(PyExc_ValueError,
1158 "mmap invalid access parameter.");
1159 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001160
Victor Stinner112d48a2011-05-03 14:36:36 +02001161#ifdef __APPLE__
1162 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1163 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1164 if (fd != -1)
1165 (void)fcntl(fd, F_FULLFSYNC);
1166#endif
Neal Norwitzb5673922002-09-05 21:48:07 +00001167#ifdef HAVE_FSTAT
Martin v. Löwisc16f3bd2003-05-03 09:14:54 +00001168# ifdef __VMS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001169 /* on OpenVMS we must ensure that all bytes are written to the file */
1170 if (fd != -1) {
1171 fsync(fd);
1172 }
Martin v. Löwisc16f3bd2003-05-03 09:14:54 +00001173# endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001174 if (fd != -1 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
1175 if (map_size == 0) {
Jesus Cea8b54d6d2012-09-10 00:22:39 +02001176 if (st.st_size == 0) {
1177 PyErr_SetString(PyExc_ValueError,
1178 "cannot mmap an empty file");
1179 return NULL;
1180 }
Antoine Pitrou8a0eede2011-01-20 21:20:18 +00001181 if (offset >= st.st_size) {
1182 PyErr_SetString(PyExc_ValueError,
1183 "mmap offset is greater than file size");
1184 return NULL;
1185 }
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001186 if (st.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001187 PyErr_SetString(PyExc_ValueError,
1188 "mmap length is too large");
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001189 return NULL;
1190 }
1191 map_size = (Py_ssize_t) (st.st_size - offset);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001192 } else if (offset > st.st_size || st.st_size - offset < map_size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001193 PyErr_SetString(PyExc_ValueError,
1194 "mmap length is greater than file size");
1195 return NULL;
1196 }
1197 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001198#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001199 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1200 if (m_obj == NULL) {return NULL;}
1201 m_obj->data = NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001202 m_obj->size = map_size;
1203 m_obj->pos = 0;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001204 m_obj->offset = offset;
1205 if (fd == -1) {
1206 m_obj->fd = -1;
1207 /* Assume the caller wants to map anonymous memory.
1208 This is the same behaviour as Windows. mmap.mmap(-1, size)
1209 on both Windows and Unix map anonymous memory.
1210 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001211#ifdef MAP_ANONYMOUS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001212 /* BSD way to map anonymous memory */
1213 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001214#else
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001215 /* SVR4 method to map anonymous memory is to open /dev/zero */
1216 fd = devzero = open("/dev/zero", O_RDWR);
1217 if (devzero == -1) {
1218 Py_DECREF(m_obj);
1219 PyErr_SetFromErrno(mmap_module_error);
1220 return NULL;
1221 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001222#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001223 } else {
1224 m_obj->fd = dup(fd);
1225 if (m_obj->fd == -1) {
1226 Py_DECREF(m_obj);
1227 PyErr_SetFromErrno(mmap_module_error);
1228 return NULL;
1229 }
1230 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001231
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001232 m_obj->data = mmap(NULL, map_size,
1233 prot, flags,
1234 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001235
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001236 if (devzero != -1) {
1237 close(devzero);
1238 }
1239
1240 if (m_obj->data == (char *)-1) {
1241 m_obj->data = NULL;
1242 Py_DECREF(m_obj);
1243 PyErr_SetFromErrno(mmap_module_error);
1244 return NULL;
1245 }
1246 m_obj->access = (access_mode)access;
1247 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001248}
1249#endif /* UNIX */
1250
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001251#ifdef MS_WINDOWS
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001252
1253/* A note on sizes and offsets: while the actual map size must hold in a
1254 Py_ssize_t, both the total file size and the start offset can be longer
1255 than a Py_ssize_t, so we use PY_LONG_LONG which is always 64-bit.
1256*/
1257
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001258static PyObject *
Georg Brandl845c4032008-01-21 14:16:46 +00001259new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001260{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001261 mmap_object *m_obj;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001262 Py_ssize_t map_size;
1263 PY_LONG_LONG offset = 0, size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001264 DWORD off_hi; /* upper 32 bits of offset */
1265 DWORD off_lo; /* lower 32 bits of offset */
1266 DWORD size_hi; /* upper 32 bits of size */
1267 DWORD size_lo; /* lower 32 bits of size */
1268 char *tagname = "";
1269 DWORD dwErr = 0;
1270 int fileno;
1271 HANDLE fh = 0;
1272 int access = (access_mode)ACCESS_DEFAULT;
1273 DWORD flProtect, dwDesiredAccess;
1274 static char *keywords[] = { "fileno", "length",
1275 "tagname",
1276 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001277
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001278 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1279 &fileno, &map_size,
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001280 &tagname, &access, &offset)) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001281 return NULL;
1282 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001283
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001284 switch((access_mode)access) {
1285 case ACCESS_READ:
1286 flProtect = PAGE_READONLY;
1287 dwDesiredAccess = FILE_MAP_READ;
1288 break;
1289 case ACCESS_DEFAULT: case ACCESS_WRITE:
1290 flProtect = PAGE_READWRITE;
1291 dwDesiredAccess = FILE_MAP_WRITE;
1292 break;
1293 case ACCESS_COPY:
1294 flProtect = PAGE_WRITECOPY;
1295 dwDesiredAccess = FILE_MAP_COPY;
1296 break;
1297 default:
1298 return PyErr_Format(PyExc_ValueError,
1299 "mmap invalid access parameter.");
1300 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001301
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001302 if (map_size < 0) {
1303 PyErr_SetString(PyExc_OverflowError,
1304 "memory mapped length must be postiive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001305 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001306 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001307 if (offset < 0) {
1308 PyErr_SetString(PyExc_OverflowError,
1309 "memory mapped offset must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001310 return NULL;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001311 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001312
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001313 /* assume -1 and 0 both mean invalid filedescriptor
1314 to 'anonymously' map memory.
1315 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1316 XXX: Should this code be added?
1317 if (fileno == 0)
1318 PyErr_Warn(PyExc_DeprecationWarning,
1319 "don't use 0 for anonymous memory");
1320 */
1321 if (fileno != -1 && fileno != 0) {
Brian Curtinba6c08e2010-08-01 15:47:53 +00001322 /* Ensure that fileno is within the CRT's valid range */
1323 if (_PyVerify_fd(fileno) == 0) {
1324 PyErr_SetFromErrno(mmap_module_error);
1325 return NULL;
1326 }
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001327 fh = (HANDLE)_get_osfhandle(fileno);
1328 if (fh==(HANDLE)-1) {
1329 PyErr_SetFromErrno(mmap_module_error);
1330 return NULL;
1331 }
1332 /* Win9x appears to need us seeked to zero */
1333 lseek(fileno, 0, SEEK_SET);
1334 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001335
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001336 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1337 if (m_obj == NULL)
1338 return NULL;
1339 /* Set every field to an invalid marker, so we can safely
1340 destruct the object in the face of failure */
1341 m_obj->data = NULL;
1342 m_obj->file_handle = INVALID_HANDLE_VALUE;
1343 m_obj->map_handle = NULL;
1344 m_obj->tagname = NULL;
1345 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001346
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001347 if (fh) {
1348 /* It is necessary to duplicate the handle, so the
1349 Python code can close it on us */
1350 if (!DuplicateHandle(
1351 GetCurrentProcess(), /* source process handle */
1352 fh, /* handle to be duplicated */
1353 GetCurrentProcess(), /* target proc handle */
1354 (LPHANDLE)&m_obj->file_handle, /* result */
1355 0, /* access - ignored due to options value */
1356 FALSE, /* inherited by child processes? */
1357 DUPLICATE_SAME_ACCESS)) { /* options */
1358 dwErr = GetLastError();
1359 Py_DECREF(m_obj);
1360 PyErr_SetFromWindowsErr(dwErr);
1361 return NULL;
1362 }
1363 if (!map_size) {
1364 DWORD low,high;
1365 low = GetFileSize(fh, &high);
1366 /* low might just happen to have the value INVALID_FILE_SIZE;
1367 so we need to check the last error also. */
1368 if (low == INVALID_FILE_SIZE &&
1369 (dwErr = GetLastError()) != NO_ERROR) {
1370 Py_DECREF(m_obj);
1371 return PyErr_SetFromWindowsErr(dwErr);
1372 }
1373
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001374 size = (((PY_LONG_LONG) high) << 32) + low;
Jesus Cea20f0ea12012-09-10 22:45:47 +02001375 if (size == 0) {
1376 PyErr_SetString(PyExc_ValueError,
1377 "cannot mmap an empty file");
Jesus Cea8e03b4c2012-09-10 22:57:34 +02001378 Py_DECREF(m_obj);
Jesus Cea20f0ea12012-09-10 22:45:47 +02001379 return NULL;
1380 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001381 if (offset >= size) {
Antoine Pitrou8a0eede2011-01-20 21:20:18 +00001382 PyErr_SetString(PyExc_ValueError,
1383 "mmap offset is greater than file size");
1384 Py_DECREF(m_obj);
1385 return NULL;
1386 }
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001387 if (size - offset > PY_SSIZE_T_MAX) {
1388 PyErr_SetString(PyExc_ValueError,
1389 "mmap length is too large");
1390 Py_DECREF(m_obj);
1391 return NULL;
1392 }
1393 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001394 } else {
1395 m_obj->size = map_size;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001396 size = offset + map_size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001397 }
1398 }
1399 else {
1400 m_obj->size = map_size;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001401 size = offset + map_size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001402 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001403
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001404 /* set the initial position */
1405 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001406
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001407 /* set the tag name */
1408 if (tagname != NULL && *tagname != '\0') {
1409 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1410 if (m_obj->tagname == NULL) {
1411 PyErr_NoMemory();
1412 Py_DECREF(m_obj);
1413 return NULL;
1414 }
1415 strcpy(m_obj->tagname, tagname);
1416 }
1417 else
1418 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001419
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001420 m_obj->access = (access_mode)access;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001421 size_hi = (DWORD)(size >> 32);
1422 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001423 off_hi = (DWORD)(offset >> 32);
1424 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001425 /* For files, it would be sufficient to pass 0 as size.
1426 For anonymous maps, we have to pass the size explicitly. */
1427 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1428 NULL,
1429 flProtect,
1430 size_hi,
1431 size_lo,
1432 m_obj->tagname);
1433 if (m_obj->map_handle != NULL) {
1434 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1435 dwDesiredAccess,
1436 off_hi,
1437 off_lo,
1438 m_obj->size);
1439 if (m_obj->data != NULL)
1440 return (PyObject *)m_obj;
1441 else {
1442 dwErr = GetLastError();
1443 CloseHandle(m_obj->map_handle);
1444 m_obj->map_handle = NULL;
1445 }
1446 } else
1447 dwErr = GetLastError();
1448 Py_DECREF(m_obj);
1449 PyErr_SetFromWindowsErr(dwErr);
1450 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001451}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001452#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001453
Neal Norwitze9ac0bb2006-08-13 18:11:27 +00001454static void
1455setint(PyObject *d, const char *name, long value)
1456{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001457 PyObject *o = PyInt_FromLong(value);
1458 if (o && PyDict_SetItemString(d, name, o) == 0) {
1459 Py_DECREF(o);
1460 }
Neal Norwitze9ac0bb2006-08-13 18:11:27 +00001461}
1462
Mark Hammond62b1ab12002-07-23 06:31:15 +00001463PyMODINIT_FUNC
Georg Brandl845c4032008-01-21 14:16:46 +00001464initmmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001465{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001466 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001467
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001468 if (PyType_Ready(&mmap_object_type) < 0)
1469 return;
Tim Peters2caf8df2001-01-14 05:05:51 +00001470
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001471 module = Py_InitModule("mmap", NULL);
1472 if (module == NULL)
1473 return;
1474 dict = PyModule_GetDict(module);
1475 if (!dict)
1476 return;
1477 mmap_module_error = PyErr_NewException("mmap.error",
1478 PyExc_EnvironmentError , NULL);
1479 if (mmap_module_error == NULL)
1480 return;
1481 PyDict_SetItemString(dict, "error", mmap_module_error);
1482 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001483#ifdef PROT_EXEC
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001484 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001485#endif
1486#ifdef PROT_READ
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001487 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001488#endif
1489#ifdef PROT_WRITE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001490 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001491#endif
1492
1493#ifdef MAP_SHARED
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001494 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001495#endif
1496#ifdef MAP_PRIVATE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001497 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001498#endif
1499#ifdef MAP_DENYWRITE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001500 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001501#endif
1502#ifdef MAP_EXECUTABLE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001503 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001504#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001505#ifdef MAP_ANONYMOUS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001506 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1507 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001508#endif
1509
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001510 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001511
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001512 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Travis E. Oliphant8feafab2007-10-23 02:40:56 +00001513
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001514 setint(dict, "ACCESS_READ", ACCESS_READ);
1515 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1516 setint(dict, "ACCESS_COPY", ACCESS_COPY);
Tim Peters5ebfd362001-11-13 23:11:19 +00001517}