blob: 2ae52c7b28df79bbad4b0f265efc3d6473df7340 [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
Serhiy Storchaka5e793212017-04-15 20:11:12 +0300787 if (_PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000788 return NULL;
789 }
Serhiy Storchakae41390a2017-04-08 11:48:57 +0300790 slicelen = _PySlice_AdjustIndices(self->size, &start, &stop, step);
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
Serhiy Storchaka5e793212017-04-15 20:11:12 +0300942 if (_PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000943 return -1;
944 }
Serhiy Storchakae41390a2017-04-08 11:48:57 +0300945 slicelen = _PySlice_AdjustIndices(self->size, &start, &stop, step);
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000946 if (value == NULL) {
947 PyErr_SetString(PyExc_TypeError,
948 "mmap object doesn't support slice deletion");
949 return -1;
950 }
951 if (!PyString_Check(value)) {
952 PyErr_SetString(PyExc_IndexError,
953 "mmap slice assignment must be a string");
954 return -1;
955 }
956 if (PyString_Size(value) != slicelen) {
957 PyErr_SetString(PyExc_IndexError,
958 "mmap slice assignment is wrong size");
959 return -1;
960 }
961 if (!is_writeable(self))
962 return -1;
Thomas Wouters3ccec682007-08-28 15:28:19 +0000963
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000964 if (slicelen == 0)
965 return 0;
966 else if (step == 1) {
967 const char *buf = PyString_AsString(value);
968
969 if (buf == NULL)
970 return -1;
971 memcpy(self->data + start, buf, slicelen);
972 return 0;
973 }
974 else {
975 Py_ssize_t cur, i;
976 const char *buf = PyString_AsString(value);
977
978 if (buf == NULL)
979 return -1;
980 for (cur = start, i = 0; i < slicelen;
981 cur += step, i++) {
982 self->data[cur] = buf[i];
983 }
984 return 0;
985 }
986 }
987 else {
988 PyErr_SetString(PyExc_TypeError,
989 "mmap indices must be integer");
990 return -1;
991 }
Thomas Wouters3ccec682007-08-28 15:28:19 +0000992}
993
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000994static PySequenceMethods mmap_as_sequence = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000995 (lenfunc)mmap_length, /*sq_length*/
996 (binaryfunc)mmap_concat, /*sq_concat*/
997 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
998 (ssizeargfunc)mmap_item, /*sq_item*/
999 (ssizessizeargfunc)mmap_slice, /*sq_slice*/
1000 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
1001 (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001002};
1003
Thomas Wouters3ccec682007-08-28 15:28:19 +00001004static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001005 (lenfunc)mmap_length,
1006 (binaryfunc)mmap_subscript,
1007 (objobjargproc)mmap_ass_subscript,
Thomas Wouters3ccec682007-08-28 15:28:19 +00001008};
1009
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001010static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001011 (readbufferproc)mmap_buffer_getreadbuf,
1012 (writebufferproc)mmap_buffer_getwritebuf,
1013 (segcountproc)mmap_buffer_getsegcount,
1014 (charbufferproc)mmap_buffer_getcharbuffer,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001015};
1016
Georg Brandl845c4032008-01-21 14:16:46 +00001017static PyObject *
1018new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1019
Georg Brandlef928022008-01-20 14:50:05 +00001020PyDoc_STRVAR(mmap_doc,
1021"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1022\n\
1023Maps length bytes from the file specified by the file handle fileno,\n\
1024and returns a mmap object. If length is larger than the current size\n\
1025of the file, the file is extended to contain length bytes. If length\n\
1026is 0, the maximum length of the map is the current size of the file,\n\
1027except that if the file is empty Windows raises an exception (you cannot\n\
1028create an empty mapping on Windows).\n\
1029\n\
1030Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1031\n\
1032Maps length bytes from the file specified by the file descriptor fileno,\n\
1033and returns a mmap object. If length is 0, the maximum length of the map\n\
1034will be the current size of the file when mmap is called.\n\
1035flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1036private copy-on-write mapping, so changes to the contents of the mmap\n\
Thomas Heller66260992008-08-19 17:47:13 +00001037object will be private to this process, and MAP_SHARED creates a mapping\n\
Georg Brandlef928022008-01-20 14:50:05 +00001038that's shared with all other processes mapping the same areas of the file.\n\
1039The default value is MAP_SHARED.\n\
1040\n\
1041To map anonymous memory, pass -1 as the fileno (both versions).");
1042
1043
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001044static PyTypeObject mmap_object_type = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001045 PyVarObject_HEAD_INIT(NULL, 0)
1046 "mmap.mmap", /* tp_name */
1047 sizeof(mmap_object), /* tp_size */
1048 0, /* tp_itemsize */
1049 /* methods */
1050 (destructor) mmap_object_dealloc, /* tp_dealloc */
1051 0, /* tp_print */
1052 0, /* tp_getattr */
1053 0, /* tp_setattr */
1054 0, /* tp_compare */
1055 0, /* tp_repr */
1056 0, /* tp_as_number */
1057 &mmap_as_sequence, /*tp_as_sequence*/
1058 &mmap_as_mapping, /*tp_as_mapping*/
1059 0, /*tp_hash*/
1060 0, /*tp_call*/
1061 0, /*tp_str*/
1062 PyObject_GenericGetAttr, /*tp_getattro*/
1063 0, /*tp_setattro*/
1064 &mmap_as_buffer, /*tp_as_buffer*/
1065 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
1066 mmap_doc, /*tp_doc*/
1067 0, /* tp_traverse */
1068 0, /* tp_clear */
1069 0, /* tp_richcompare */
1070 0, /* tp_weaklistoffset */
1071 0, /* tp_iter */
1072 0, /* tp_iternext */
1073 mmap_object_methods, /* tp_methods */
1074 0, /* tp_members */
1075 0, /* tp_getset */
1076 0, /* tp_base */
1077 0, /* tp_dict */
1078 0, /* tp_descr_get */
1079 0, /* tp_descr_set */
1080 0, /* tp_dictoffset */
1081 0, /* tp_init */
1082 PyType_GenericAlloc, /* tp_alloc */
1083 new_mmap_object, /* tp_new */
1084 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001085};
1086
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001087
Tim Petersec0a5f02006-02-16 23:47:20 +00001088#ifdef UNIX
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001089#ifdef HAVE_LARGEFILE_SUPPORT
1090#define _Py_PARSE_OFF_T "L"
1091#else
1092#define _Py_PARSE_OFF_T "l"
1093#endif
1094
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001095static PyObject *
Georg Brandl845c4032008-01-21 14:16:46 +00001096new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001097{
Neal Norwitzb5673922002-09-05 21:48:07 +00001098#ifdef HAVE_FSTAT
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001099 struct stat st;
Neal Norwitzb5673922002-09-05 21:48:07 +00001100#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001101 mmap_object *m_obj;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001102 Py_ssize_t map_size;
1103 off_t offset = 0;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001104 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1105 int devzero = -1;
1106 int access = (int)ACCESS_DEFAULT;
1107 static char *keywords[] = {"fileno", "length",
1108 "flags", "prot",
1109 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001110
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001111 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1112 &fd, &map_size, &flags, &prot,
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001113 &access, &offset))
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001114 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001115 if (map_size < 0) {
1116 PyErr_SetString(PyExc_OverflowError,
Miss Islington (bot)7ee09362018-03-20 23:55:44 -07001117 "memory mapped length must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001118 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001119 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001120 if (offset < 0) {
1121 PyErr_SetString(PyExc_OverflowError,
1122 "memory mapped offset must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001123 return NULL;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001124 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001125
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001126 if ((access != (int)ACCESS_DEFAULT) &&
1127 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1128 return PyErr_Format(PyExc_ValueError,
1129 "mmap can't specify both access and flags, prot.");
1130 switch ((access_mode)access) {
1131 case ACCESS_READ:
1132 flags = MAP_SHARED;
1133 prot = PROT_READ;
1134 break;
1135 case ACCESS_WRITE:
1136 flags = MAP_SHARED;
1137 prot = PROT_READ | PROT_WRITE;
1138 break;
1139 case ACCESS_COPY:
1140 flags = MAP_PRIVATE;
1141 prot = PROT_READ | PROT_WRITE;
1142 break;
1143 case ACCESS_DEFAULT:
Antoine Pitroud6f3a3e2011-03-06 02:03:34 +01001144 /* map prot to access type */
1145 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1146 /* ACCESS_DEFAULT */
1147 }
1148 else if (prot & PROT_WRITE) {
1149 access = ACCESS_WRITE;
1150 }
1151 else {
1152 access = ACCESS_READ;
1153 }
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001154 break;
1155 default:
1156 return PyErr_Format(PyExc_ValueError,
1157 "mmap invalid access parameter.");
1158 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001159
Victor Stinner112d48a2011-05-03 14:36:36 +02001160#ifdef __APPLE__
1161 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1162 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1163 if (fd != -1)
1164 (void)fcntl(fd, F_FULLFSYNC);
1165#endif
Neal Norwitzb5673922002-09-05 21:48:07 +00001166#ifdef HAVE_FSTAT
Martin v. Löwisc16f3bd2003-05-03 09:14:54 +00001167# ifdef __VMS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001168 /* on OpenVMS we must ensure that all bytes are written to the file */
1169 if (fd != -1) {
1170 fsync(fd);
1171 }
Martin v. Löwisc16f3bd2003-05-03 09:14:54 +00001172# endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001173 if (fd != -1 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
1174 if (map_size == 0) {
Jesus Cea8b54d6d2012-09-10 00:22:39 +02001175 if (st.st_size == 0) {
1176 PyErr_SetString(PyExc_ValueError,
1177 "cannot mmap an empty file");
1178 return NULL;
1179 }
Antoine Pitrou8a0eede2011-01-20 21:20:18 +00001180 if (offset >= st.st_size) {
1181 PyErr_SetString(PyExc_ValueError,
1182 "mmap offset is greater than file size");
1183 return NULL;
1184 }
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001185 if (st.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001186 PyErr_SetString(PyExc_ValueError,
1187 "mmap length is too large");
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001188 return NULL;
1189 }
1190 map_size = (Py_ssize_t) (st.st_size - offset);
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001191 } else if (offset > st.st_size || st.st_size - offset < map_size) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001192 PyErr_SetString(PyExc_ValueError,
1193 "mmap length is greater than file size");
1194 return NULL;
1195 }
1196 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001197#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001198 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1199 if (m_obj == NULL) {return NULL;}
1200 m_obj->data = NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001201 m_obj->size = map_size;
1202 m_obj->pos = 0;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001203 m_obj->offset = offset;
1204 if (fd == -1) {
1205 m_obj->fd = -1;
1206 /* Assume the caller wants to map anonymous memory.
1207 This is the same behaviour as Windows. mmap.mmap(-1, size)
1208 on both Windows and Unix map anonymous memory.
1209 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001210#ifdef MAP_ANONYMOUS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001211 /* BSD way to map anonymous memory */
1212 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001213#else
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001214 /* SVR4 method to map anonymous memory is to open /dev/zero */
1215 fd = devzero = open("/dev/zero", O_RDWR);
1216 if (devzero == -1) {
1217 Py_DECREF(m_obj);
1218 PyErr_SetFromErrno(mmap_module_error);
1219 return NULL;
1220 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001221#endif
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001222 } else {
1223 m_obj->fd = dup(fd);
1224 if (m_obj->fd == -1) {
1225 Py_DECREF(m_obj);
1226 PyErr_SetFromErrno(mmap_module_error);
1227 return NULL;
1228 }
1229 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001230
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001231 m_obj->data = mmap(NULL, map_size,
1232 prot, flags,
1233 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001234
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001235 if (devzero != -1) {
1236 close(devzero);
1237 }
1238
1239 if (m_obj->data == (char *)-1) {
1240 m_obj->data = NULL;
1241 Py_DECREF(m_obj);
1242 PyErr_SetFromErrno(mmap_module_error);
1243 return NULL;
1244 }
1245 m_obj->access = (access_mode)access;
1246 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001247}
1248#endif /* UNIX */
1249
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001250#ifdef MS_WINDOWS
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001251
1252/* A note on sizes and offsets: while the actual map size must hold in a
1253 Py_ssize_t, both the total file size and the start offset can be longer
1254 than a Py_ssize_t, so we use PY_LONG_LONG which is always 64-bit.
1255*/
1256
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001257static PyObject *
Georg Brandl845c4032008-01-21 14:16:46 +00001258new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001259{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001260 mmap_object *m_obj;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001261 Py_ssize_t map_size;
1262 PY_LONG_LONG offset = 0, size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001263 DWORD off_hi; /* upper 32 bits of offset */
1264 DWORD off_lo; /* lower 32 bits of offset */
1265 DWORD size_hi; /* upper 32 bits of size */
1266 DWORD size_lo; /* lower 32 bits of size */
1267 char *tagname = "";
1268 DWORD dwErr = 0;
1269 int fileno;
1270 HANDLE fh = 0;
1271 int access = (access_mode)ACCESS_DEFAULT;
1272 DWORD flProtect, dwDesiredAccess;
1273 static char *keywords[] = { "fileno", "length",
1274 "tagname",
1275 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001276
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001277 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1278 &fileno, &map_size,
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001279 &tagname, &access, &offset)) {
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001280 return NULL;
1281 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001282
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001283 switch((access_mode)access) {
1284 case ACCESS_READ:
1285 flProtect = PAGE_READONLY;
1286 dwDesiredAccess = FILE_MAP_READ;
1287 break;
1288 case ACCESS_DEFAULT: case ACCESS_WRITE:
1289 flProtect = PAGE_READWRITE;
1290 dwDesiredAccess = FILE_MAP_WRITE;
1291 break;
1292 case ACCESS_COPY:
1293 flProtect = PAGE_WRITECOPY;
1294 dwDesiredAccess = FILE_MAP_COPY;
1295 break;
1296 default:
1297 return PyErr_Format(PyExc_ValueError,
1298 "mmap invalid access parameter.");
1299 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001300
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001301 if (map_size < 0) {
1302 PyErr_SetString(PyExc_OverflowError,
Miss Islington (bot)7ee09362018-03-20 23:55:44 -07001303 "memory mapped length must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001304 return NULL;
Benjamin Peterson1df2cbe2016-10-05 21:45:48 -07001305 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001306 if (offset < 0) {
1307 PyErr_SetString(PyExc_OverflowError,
1308 "memory mapped offset must be positive");
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001309 return NULL;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001310 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001311
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001312 /* assume -1 and 0 both mean invalid filedescriptor
1313 to 'anonymously' map memory.
1314 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1315 XXX: Should this code be added?
1316 if (fileno == 0)
1317 PyErr_Warn(PyExc_DeprecationWarning,
1318 "don't use 0 for anonymous memory");
1319 */
1320 if (fileno != -1 && fileno != 0) {
Brian Curtinba6c08e2010-08-01 15:47:53 +00001321 /* Ensure that fileno is within the CRT's valid range */
1322 if (_PyVerify_fd(fileno) == 0) {
1323 PyErr_SetFromErrno(mmap_module_error);
1324 return NULL;
1325 }
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001326 fh = (HANDLE)_get_osfhandle(fileno);
1327 if (fh==(HANDLE)-1) {
1328 PyErr_SetFromErrno(mmap_module_error);
1329 return NULL;
1330 }
1331 /* Win9x appears to need us seeked to zero */
1332 lseek(fileno, 0, SEEK_SET);
1333 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001334
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001335 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1336 if (m_obj == NULL)
1337 return NULL;
1338 /* Set every field to an invalid marker, so we can safely
1339 destruct the object in the face of failure */
1340 m_obj->data = NULL;
1341 m_obj->file_handle = INVALID_HANDLE_VALUE;
1342 m_obj->map_handle = NULL;
1343 m_obj->tagname = NULL;
1344 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001345
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001346 if (fh) {
1347 /* It is necessary to duplicate the handle, so the
1348 Python code can close it on us */
1349 if (!DuplicateHandle(
1350 GetCurrentProcess(), /* source process handle */
1351 fh, /* handle to be duplicated */
1352 GetCurrentProcess(), /* target proc handle */
1353 (LPHANDLE)&m_obj->file_handle, /* result */
1354 0, /* access - ignored due to options value */
1355 FALSE, /* inherited by child processes? */
1356 DUPLICATE_SAME_ACCESS)) { /* options */
1357 dwErr = GetLastError();
1358 Py_DECREF(m_obj);
1359 PyErr_SetFromWindowsErr(dwErr);
1360 return NULL;
1361 }
1362 if (!map_size) {
1363 DWORD low,high;
1364 low = GetFileSize(fh, &high);
1365 /* low might just happen to have the value INVALID_FILE_SIZE;
1366 so we need to check the last error also. */
1367 if (low == INVALID_FILE_SIZE &&
1368 (dwErr = GetLastError()) != NO_ERROR) {
1369 Py_DECREF(m_obj);
1370 return PyErr_SetFromWindowsErr(dwErr);
1371 }
1372
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001373 size = (((PY_LONG_LONG) high) << 32) + low;
Jesus Cea20f0ea12012-09-10 22:45:47 +02001374 if (size == 0) {
1375 PyErr_SetString(PyExc_ValueError,
1376 "cannot mmap an empty file");
Jesus Cea8e03b4c2012-09-10 22:57:34 +02001377 Py_DECREF(m_obj);
Jesus Cea20f0ea12012-09-10 22:45:47 +02001378 return NULL;
1379 }
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001380 if (offset >= size) {
Antoine Pitrou8a0eede2011-01-20 21:20:18 +00001381 PyErr_SetString(PyExc_ValueError,
1382 "mmap offset is greater than file size");
1383 Py_DECREF(m_obj);
1384 return NULL;
1385 }
Richard Oudkerk36b9d412013-02-13 12:05:14 +00001386 if (size - offset > PY_SSIZE_T_MAX) {
1387 PyErr_SetString(PyExc_ValueError,
1388 "mmap length is too large");
1389 Py_DECREF(m_obj);
1390 return NULL;
1391 }
1392 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001393 } else {
1394 m_obj->size = map_size;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001395 size = offset + map_size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001396 }
1397 }
1398 else {
1399 m_obj->size = map_size;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001400 size = offset + map_size;
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001401 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001402
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001403 /* set the initial position */
1404 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001405
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001406 /* set the tag name */
1407 if (tagname != NULL && *tagname != '\0') {
1408 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1409 if (m_obj->tagname == NULL) {
1410 PyErr_NoMemory();
1411 Py_DECREF(m_obj);
1412 return NULL;
1413 }
1414 strcpy(m_obj->tagname, tagname);
1415 }
1416 else
1417 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001418
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001419 m_obj->access = (access_mode)access;
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001420 size_hi = (DWORD)(size >> 32);
1421 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001422 off_hi = (DWORD)(offset >> 32);
1423 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001424 /* For files, it would be sufficient to pass 0 as size.
1425 For anonymous maps, we have to pass the size explicitly. */
1426 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1427 NULL,
1428 flProtect,
1429 size_hi,
1430 size_lo,
1431 m_obj->tagname);
1432 if (m_obj->map_handle != NULL) {
1433 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1434 dwDesiredAccess,
1435 off_hi,
1436 off_lo,
1437 m_obj->size);
1438 if (m_obj->data != NULL)
1439 return (PyObject *)m_obj;
1440 else {
1441 dwErr = GetLastError();
1442 CloseHandle(m_obj->map_handle);
1443 m_obj->map_handle = NULL;
1444 }
1445 } else
1446 dwErr = GetLastError();
1447 Py_DECREF(m_obj);
1448 PyErr_SetFromWindowsErr(dwErr);
1449 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001450}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001451#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001452
Neal Norwitze9ac0bb2006-08-13 18:11:27 +00001453static void
1454setint(PyObject *d, const char *name, long value)
1455{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001456 PyObject *o = PyInt_FromLong(value);
1457 if (o && PyDict_SetItemString(d, name, o) == 0) {
1458 Py_DECREF(o);
1459 }
Neal Norwitze9ac0bb2006-08-13 18:11:27 +00001460}
1461
Mark Hammond62b1ab12002-07-23 06:31:15 +00001462PyMODINIT_FUNC
Georg Brandl845c4032008-01-21 14:16:46 +00001463initmmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001464{
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001465 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001466
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001467 if (PyType_Ready(&mmap_object_type) < 0)
1468 return;
Tim Peters2caf8df2001-01-14 05:05:51 +00001469
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001470 module = Py_InitModule("mmap", NULL);
1471 if (module == NULL)
1472 return;
1473 dict = PyModule_GetDict(module);
1474 if (!dict)
1475 return;
1476 mmap_module_error = PyErr_NewException("mmap.error",
1477 PyExc_EnvironmentError , NULL);
1478 if (mmap_module_error == NULL)
1479 return;
1480 PyDict_SetItemString(dict, "error", mmap_module_error);
1481 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001482#ifdef PROT_EXEC
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001483 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001484#endif
1485#ifdef PROT_READ
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001486 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001487#endif
1488#ifdef PROT_WRITE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001489 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001490#endif
1491
1492#ifdef MAP_SHARED
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001493 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001494#endif
1495#ifdef MAP_PRIVATE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001496 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001497#endif
1498#ifdef MAP_DENYWRITE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001499 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001500#endif
1501#ifdef MAP_EXECUTABLE
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001502 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001503#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001504#ifdef MAP_ANONYMOUS
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001505 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1506 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001507#endif
1508
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001509 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001510
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001511 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Travis E. Oliphant8feafab2007-10-23 02:40:56 +00001512
Antoine Pitrouc83ea132010-05-09 14:46:46 +00001513 setint(dict, "ACCESS_READ", ACCESS_READ);
1514 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1515 setint(dict, "ACCESS_COPY", ACCESS_COPY);
Tim Peters5ebfd362001-11-13 23:11:19 +00001516}