blob: 7a94464e7b227d26ca29327f35d7ab2d734dc043 [file] [log] [blame]
Guido van Rossum09fdf072000-03-31 01:17:07 +00001/*
2 / Author: Sam Rushing <rushing@nightmare.com>
Andrew M. Kuchling10f9c072001-11-05 21:25:42 +00003 / Hacked for Unix by AMK
Guido van Rossum09fdf072000-03-31 01:17:07 +00004 / $Id$
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00005
Guido van Rossum8ce8a782007-11-01 19:42:39 +00006 / Modified to support mmap with offset - to map a 'window' of a file
7 / Author: Yotam Medini yotamm@mellanox.co.il
8 /
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00009 / mmapmodule.cpp -- map a view of a file into memory
10 /
11 / todo: need permission flags, perhaps a 'chsize' analog
12 / not all functions check range yet!!!
13 /
14 /
Mark Hammond071864a2000-07-30 02:46:26 +000015 / This version of mmapmodule.c has been changed significantly
16 / from the original mmapfile.c on which it was based.
17 / The original version of mmapfile is maintained by Sam at
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000018 / ftp://squirl.nightmare.com/pub/python/python-ext.
19*/
20
Martin v. Löwiscfe7e092006-02-17 06:59:14 +000021#define PY_SSIZE_T_CLEAN
Guido van Rossum09fdf072000-03-31 01:17:07 +000022#include <Python.h>
Antoine Pitrouc53204b2013-08-05 23:17:30 +020023#include "structmember.h"
Guido van Rossum09fdf072000-03-31 01:17:07 +000024
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000025#ifndef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000026#define UNIX
Benjamin Petersonf6b5cad2015-08-02 12:15:30 -070027# ifdef HAVE_FCNTL_H
Victor Stinnera6cd0cf2011-05-02 01:05:37 +020028# include <fcntl.h>
Benjamin Petersonf6b5cad2015-08-02 12:15:30 -070029# endif /* HAVE_FCNTL_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000030#endif
31
Martin v. Löwis6238d2b2002-06-30 15:26:10 +000032#ifdef MS_WINDOWS
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000033#include <windows.h>
Fred Drake145f96e2000-10-01 17:50:46 +000034static int
35my_getpagesize(void)
36{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000037 SYSTEM_INFO si;
38 GetSystemInfo(&si);
39 return si.dwPageSize;
Fred Drake145f96e2000-10-01 17:50:46 +000040}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000041
42static int
43my_getallocationgranularity (void)
44{
45
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000046 SYSTEM_INFO si;
47 GetSystemInfo(&si);
48 return si.dwAllocationGranularity;
Guido van Rossum8ce8a782007-11-01 19:42:39 +000049}
50
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000051#endif
52
53#ifdef UNIX
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000054#include <sys/mman.h>
Andrew M. Kuchling7b9fb922000-06-17 22:41:22 +000055#include <sys/stat.h>
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000056
Fred Drake145f96e2000-10-01 17:50:46 +000057#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
58static int
59my_getpagesize(void)
60{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000061 return sysconf(_SC_PAGESIZE);
Fred Drake145f96e2000-10-01 17:50:46 +000062}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000063
64#define my_getallocationgranularity my_getpagesize
Fred Drake145f96e2000-10-01 17:50:46 +000065#else
66#define my_getpagesize getpagesize
67#endif
68
Guido van Rossum4b36e6b2000-09-25 13:16:15 +000069#endif /* UNIX */
70
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000071#include <string.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +000072
73#ifdef HAVE_SYS_TYPES_H
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000074#include <sys/types.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +000075#endif /* HAVE_SYS_TYPES_H */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000076
Neal Norwitz3eaf2b52006-02-16 08:08:54 +000077/* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +000078#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
79# define MAP_ANONYMOUS MAP_ANON
80#endif
81
Tim Peters5ebfd362001-11-13 23:11:19 +000082typedef enum
83{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000084 ACCESS_DEFAULT,
85 ACCESS_READ,
86 ACCESS_WRITE,
87 ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +000088} access_mode;
89
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +000090typedef struct {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000091 PyObject_HEAD
92 char * data;
Benjamin Petersoncd04db02016-10-05 21:45:48 -070093 Py_ssize_t size;
94 Py_ssize_t pos; /* relative to offset */
Antoine Pitrou97696cb2011-02-21 23:46:27 +000095#ifdef MS_WINDOWS
Benjamin Petersonaf580df2016-09-06 10:46:49 -070096 long long offset;
Antoine Pitrou97696cb2011-02-21 23:46:27 +000097#else
98 off_t offset;
99#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000100 int exports;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000101
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000102#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000103 HANDLE map_handle;
104 HANDLE file_handle;
105 char * tagname;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000106#endif
107
108#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000109 int fd;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000110#endif
Tim Peters5ebfd362001-11-13 23:11:19 +0000111
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200112 PyObject *weakreflist;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000113 access_mode access;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000114} mmap_object;
115
Tim Peters5ebfd362001-11-13 23:11:19 +0000116
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000117static void
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000118mmap_object_dealloc(mmap_object *m_obj)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000119{
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000120#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000121 if (m_obj->data != NULL)
122 UnmapViewOfFile (m_obj->data);
123 if (m_obj->map_handle != NULL)
124 CloseHandle (m_obj->map_handle);
125 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
126 CloseHandle (m_obj->file_handle);
127 if (m_obj->tagname)
128 PyMem_Free(m_obj->tagname);
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000129#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000130
131#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000132 if (m_obj->fd >= 0)
133 (void) close(m_obj->fd);
134 if (m_obj->data!=NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000135 munmap(m_obj->data, m_obj->size);
136 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000137#endif /* UNIX */
138
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200139 if (m_obj->weakreflist != NULL)
140 PyObject_ClearWeakRefs((PyObject *) m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000141 Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000142}
143
144static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000145mmap_close_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000146{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000147 if (self->exports > 0) {
148 PyErr_SetString(PyExc_BufferError, "cannot close "\
149 "exported pointers exist");
150 return NULL;
151 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000152#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000153 /* For each resource we maintain, we need to check
154 the value is valid, and if so, free the resource
155 and set the member value to an invalid value so
156 the dealloc does not attempt to resource clearing
157 again.
158 TODO - should we check for errors in the close operations???
159 */
160 if (self->data != NULL) {
161 UnmapViewOfFile(self->data);
162 self->data = NULL;
163 }
164 if (self->map_handle != NULL) {
165 CloseHandle(self->map_handle);
166 self->map_handle = NULL;
167 }
168 if (self->file_handle != INVALID_HANDLE_VALUE) {
169 CloseHandle(self->file_handle);
170 self->file_handle = INVALID_HANDLE_VALUE;
171 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000172#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000173
174#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000175 if (0 <= self->fd)
176 (void) close(self->fd);
177 self->fd = -1;
178 if (self->data != NULL) {
179 munmap(self->data, self->size);
180 self->data = NULL;
181 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000182#endif
183
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200184 Py_RETURN_NONE;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000185}
186
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000187#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000188#define CHECK_VALID(err) \
189do { \
190 if (self->map_handle == NULL) { \
191 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
192 return err; \
193 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000194} while (0)
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000195#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000196
197#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000198#define CHECK_VALID(err) \
199do { \
200 if (self->data == NULL) { \
201 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
202 return err; \
203 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000204} while (0)
205#endif /* UNIX */
206
207static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000208mmap_read_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000209 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000210{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000211 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700212 if (self->pos >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000213 PyErr_SetString(PyExc_ValueError, "read byte out of range");
214 return NULL;
215 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700216 return PyLong_FromLong((unsigned char)self->data[self->pos++]);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000217}
218
219static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000220mmap_read_line_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000221 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000222{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700223 Py_ssize_t remaining;
224 char *start, *eol;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000225 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000226
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000227 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000228
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700229 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
230 if (!remaining)
231 return PyBytes_FromString("");
232 start = self->data + self->pos;
233 eol = memchr(start, '\n', remaining);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000234 if (!eol)
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700235 eol = self->data + self->size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000236 else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700237 ++eol; /* advance past newline */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000238 result = PyBytes_FromStringAndSize(start, (eol - start));
239 self->pos += (eol - start);
240 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000241}
242
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200243/* Basically the "n" format code with the ability to turn None into -1. */
244static int
245mmap_convert_ssize_t(PyObject *obj, void *result) {
246 Py_ssize_t limit;
247 if (obj == Py_None) {
248 limit = -1;
249 }
250 else if (PyNumber_Check(obj)) {
251 limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
252 if (limit == -1 && PyErr_Occurred())
253 return 0;
254 }
255 else {
256 PyErr_Format(PyExc_TypeError,
257 "integer argument expected, got '%.200s'",
258 Py_TYPE(obj)->tp_name);
259 return 0;
260 }
261 *((Py_ssize_t *)result) = limit;
262 return 1;
263}
264
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000265static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000266mmap_read_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000267 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000268{
Benjamin Peterson8f1cdc62016-10-05 23:32:09 -0700269 Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000270 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000271
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000272 CHECK_VALID(NULL);
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200273 if (!PyArg_ParseTuple(args, "|O&:read", mmap_convert_ssize_t, &num_bytes))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000274 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000275
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000276 /* silently 'adjust' out-of-range requests */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700277 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
278 if (num_bytes < 0 || num_bytes > remaining)
279 num_bytes = remaining;
280 result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000281 self->pos += num_bytes;
282 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000283}
284
285static PyObject *
Georg Brandlfceab5a2008-01-19 20:08:23 +0000286mmap_gfind(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000287 PyObject *args,
288 int reverse)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000289{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000290 Py_ssize_t start = self->pos;
291 Py_ssize_t end = self->size;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200292 Py_buffer view;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000293
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000294 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200295 if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
296 &view, &start, &end)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000297 return NULL;
298 } else {
299 const char *p, *start_p, *end_p;
300 int sign = reverse ? -1 : 1;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200301 const char *needle = view.buf;
302 Py_ssize_t len = view.len;
Greg Stein834f4dd2001-05-14 09:32:26 +0000303
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000304 if (start < 0)
305 start += self->size;
306 if (start < 0)
307 start = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700308 else if (start > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000309 start = self->size;
Greg Stein834f4dd2001-05-14 09:32:26 +0000310
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000311 if (end < 0)
312 end += self->size;
313 if (end < 0)
314 end = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700315 else if (end > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000316 end = self->size;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000317
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000318 start_p = self->data + start;
319 end_p = self->data + end;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000320
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000321 for (p = (reverse ? end_p - len : start_p);
322 (p >= start_p) && (p + len <= end_p); p += sign) {
323 Py_ssize_t i;
324 for (i = 0; i < len && needle[i] == p[i]; ++i)
325 /* nothing */;
326 if (i == len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200327 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000328 return PyLong_FromSsize_t(p - self->data);
329 }
330 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200331 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000332 return PyLong_FromLong(-1);
333 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000334}
335
Georg Brandlfceab5a2008-01-19 20:08:23 +0000336static PyObject *
337mmap_find_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000338 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000339{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000340 return mmap_gfind(self, args, 0);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000341}
342
343static PyObject *
344mmap_rfind_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000345 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000346{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000347 return mmap_gfind(self, args, 1);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000348}
349
Tim Petersec0a5f02006-02-16 23:47:20 +0000350static int
Sean Reifscheider54cf12b2007-09-17 17:55:36 +0000351is_writable(mmap_object *self)
Tim Peters5ebfd362001-11-13 23:11:19 +0000352{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000353 if (self->access != ACCESS_READ)
354 return 1;
355 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
356 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000357}
358
Tim Petersec0a5f02006-02-16 23:47:20 +0000359static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000360is_resizeable(mmap_object *self)
361{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000362 if (self->exports > 0) {
363 PyErr_SetString(PyExc_BufferError,
364 "mmap can't resize with extant buffers exported.");
365 return 0;
366 }
367 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
368 return 1;
369 PyErr_Format(PyExc_TypeError,
370 "mmap can't resize a readonly or copy-on-write memory map.");
371 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000372}
373
374
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000375static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000376mmap_write_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000377 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000378{
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200379 Py_buffer data;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000380
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000381 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200382 if (!PyArg_ParseTuple(args, "y*:write", &data))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000383 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000384
Benjamin Peterson37768362016-10-05 23:29:07 -0700385 if (!is_writable(self)) {
386 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700387 return NULL;
Benjamin Peterson37768362016-10-05 23:29:07 -0700388 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700389
390 if (self->pos > self->size || self->size - self->pos < data.len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200391 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700392 PyErr_SetString(PyExc_ValueError, "data out of range");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000393 return NULL;
394 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200395
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700396 memcpy(&self->data[self->pos], data.buf, data.len);
397 self->pos += data.len;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200398 PyBuffer_Release(&data);
Benjamin Peterson87845bc2016-10-05 22:54:19 -0700399 return PyLong_FromSsize_t(data.len);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000400}
401
402static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000403mmap_write_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000404 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000405{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000406 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000407
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000408 CHECK_VALID(NULL);
409 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
410 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000411
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000412 if (!is_writable(self))
413 return NULL;
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000414
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000415 if (self->pos < self->size) {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700416 self->data[self->pos++] = value;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200417 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000418 }
419 else {
420 PyErr_SetString(PyExc_ValueError, "write byte out of range");
421 return NULL;
422 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000423}
Tim Petersec0a5f02006-02-16 23:47:20 +0000424
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000425static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000426mmap_size_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000427 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000428{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000429 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000430
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000431#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000432 if (self->file_handle != INVALID_HANDLE_VALUE) {
433 DWORD low,high;
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700434 long long size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000435 low = GetFileSize(self->file_handle, &high);
436 if (low == INVALID_FILE_SIZE) {
437 /* It might be that the function appears to have failed,
438 when indeed its size equals INVALID_FILE_SIZE */
439 DWORD error = GetLastError();
440 if (error != NO_ERROR)
441 return PyErr_SetFromWindowsErr(error);
442 }
443 if (!high && low < LONG_MAX)
444 return PyLong_FromLong((long)low);
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700445 size = (((long long)high)<<32) + low;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000446 return PyLong_FromLongLong(size);
447 } else {
448 return PyLong_FromSsize_t(self->size);
449 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000450#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000451
452#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000453 {
Victor Stinnere134a7f2015-03-30 10:09:31 +0200454 struct _Py_stat_struct status;
455 if (_Py_fstat(self->fd, &status) == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000456 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000457#ifdef HAVE_LARGEFILE_SUPPORT
Victor Stinnere134a7f2015-03-30 10:09:31 +0200458 return PyLong_FromLongLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000459#else
Victor Stinnere134a7f2015-03-30 10:09:31 +0200460 return PyLong_FromLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000461#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000462 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000463#endif /* UNIX */
464}
465
466/* This assumes that you want the entire file mapped,
467 / and when recreating the map will make the new file
468 / have the new size
469 /
470 / Is this really necessary? This could easily be done
471 / from python by just closing and re-opening with the
472 / new size?
473 */
474
475static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000476mmap_resize_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000477 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000478{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000479 Py_ssize_t new_size;
480 CHECK_VALID(NULL);
481 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
482 !is_resizeable(self)) {
483 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700484 }
485 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
486 PyErr_SetString(PyExc_ValueError, "new size out of range");
487 return NULL;
488 }
489
490 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000491#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000492 DWORD dwErrCode = 0;
493 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
494 /* First, unmap the file view */
495 UnmapViewOfFile(self->data);
496 self->data = NULL;
497 /* Close the mapping object */
498 CloseHandle(self->map_handle);
499 self->map_handle = NULL;
500 /* Move to the desired EOF position */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000501 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
502 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
503 off_hi = (DWORD)(self->offset >> 32);
504 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000505 SetFilePointer(self->file_handle,
506 newSizeLow, &newSizeHigh, FILE_BEGIN);
507 /* Change the size of the file */
508 SetEndOfFile(self->file_handle);
509 /* Create another mapping object and remap the file view */
510 self->map_handle = CreateFileMapping(
511 self->file_handle,
512 NULL,
513 PAGE_READWRITE,
514 0,
515 0,
516 self->tagname);
517 if (self->map_handle != NULL) {
518 self->data = (char *) MapViewOfFile(self->map_handle,
519 FILE_MAP_WRITE,
520 off_hi,
521 off_lo,
522 new_size);
523 if (self->data != NULL) {
524 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200525 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000526 } else {
527 dwErrCode = GetLastError();
528 CloseHandle(self->map_handle);
529 self->map_handle = NULL;
530 }
531 } else {
532 dwErrCode = GetLastError();
533 }
534 PyErr_SetFromWindowsErr(dwErrCode);
535 return NULL;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000536#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000537
538#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000539#ifndef HAVE_MREMAP
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000540 PyErr_SetString(PyExc_SystemError,
541 "mmap: resizing not available--no mremap()");
542 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000543#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000544 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000545
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700546 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200547 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000548 return NULL;
549 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000550
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000551#ifdef MREMAP_MAYMOVE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000552 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000553#else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700554#if defined(__NetBSD__)
555 newmap = mremap(self->data, self->size, self->data, new_size, 0);
556#else
557 newmap = mremap(self->data, self->size, new_size, 0);
558#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000559#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000560 if (newmap == (void *)-1)
561 {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200562 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000563 return NULL;
564 }
565 self->data = newmap;
566 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200567 Py_RETURN_NONE;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000568#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000569#endif /* UNIX */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000570 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000571}
572
573static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000574mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000575{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000576 CHECK_VALID(NULL);
577 return PyLong_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000578}
579
580static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000581mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000582{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000583 Py_ssize_t offset = 0;
584 Py_ssize_t size = self->size;
585 CHECK_VALID(NULL);
586 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
587 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700588 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000589 PyErr_SetString(PyExc_ValueError, "flush values out of range");
590 return NULL;
591 }
R. David Murraye194dd62010-10-18 01:14:06 +0000592
593 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
594 return PyLong_FromLong(0);
595
Christian Heimesaf98da12008-01-27 15:18:18 +0000596#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000597 return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
Christian Heimesaf98da12008-01-27 15:18:18 +0000598#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000599 /* XXX semantics of return value? */
600 /* XXX flags for msync? */
601 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200602 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000603 return NULL;
604 }
605 return PyLong_FromLong(0);
Christian Heimesaf98da12008-01-27 15:18:18 +0000606#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000607 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
608 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000609#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000610}
611
612static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000613mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000614{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000615 Py_ssize_t dist;
616 int how=0;
617 CHECK_VALID(NULL);
618 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
619 return NULL;
620 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700621 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000622 switch (how) {
623 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000624 where = dist;
625 break;
626 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700627 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000628 goto onoutofrange;
629 where = self->pos + dist;
630 break;
631 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700632 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000633 goto onoutofrange;
634 where = self->size + dist;
635 break;
636 default:
637 PyErr_SetString(PyExc_ValueError, "unknown seek type");
638 return NULL;
639 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700640 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000641 goto onoutofrange;
642 self->pos = where;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200643 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000644 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000645
Tim Peters5ebfd362001-11-13 23:11:19 +0000646 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000647 PyErr_SetString(PyExc_ValueError, "seek out of range");
648 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000649}
650
651static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000652mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000653{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700654 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000655 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700656 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000657 !is_writable(self)) {
658 return NULL;
659 } else {
660 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700661 if (dest < 0 || src < 0 || cnt < 0)
662 goto bounds;
663 if (self->size - dest < cnt || self->size - src < cnt)
664 goto bounds;
665
666 memmove(&self->data[dest], &self->data[src], cnt);
667
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200668 Py_RETURN_NONE;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700669
670 bounds:
671 PyErr_SetString(PyExc_ValueError,
672 "source, destination, or count out of range");
673 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000674 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000675}
676
Georg Brandl0bccc182010-08-01 14:50:00 +0000677static PyObject *
678mmap_closed_get(mmap_object *self)
679{
680#ifdef MS_WINDOWS
681 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
682#elif defined(UNIX)
683 return PyBool_FromLong(self->data == NULL ? 1 : 0);
684#endif
685}
686
687static PyObject *
688mmap__enter__method(mmap_object *self, PyObject *args)
689{
690 CHECK_VALID(NULL);
691
692 Py_INCREF(self);
693 return (PyObject *)self;
694}
695
696static PyObject *
697mmap__exit__method(PyObject *self, PyObject *args)
698{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200699 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200700
701 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000702}
703
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300704#ifdef MS_WINDOWS
705static PyObject *
706mmap__sizeof__method(mmap_object *self, void *unused)
707{
708 Py_ssize_t res;
709
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200710 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300711 if (self->tagname)
712 res += strlen(self->tagname) + 1;
713 return PyLong_FromSsize_t(res);
714}
715#endif
716
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000717static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000718 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
719 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
720 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
721 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
722 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
723 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
724 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
725 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
726 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
727 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
728 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
729 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
730 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
731 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000732 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
733 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300734#ifdef MS_WINDOWS
735 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
736#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000737 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000738};
739
Georg Brandl0bccc182010-08-01 14:50:00 +0000740static PyGetSetDef mmap_object_getset[] = {
741 {"closed", (getter) mmap_closed_get, NULL, NULL},
742 {NULL}
743};
744
745
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000746/* Functions for treating an mmap'ed file as a buffer */
747
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000748static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000749mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000750{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000751 CHECK_VALID(-1);
752 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
753 (self->access == ACCESS_READ), flags) < 0)
754 return -1;
755 self->exports++;
756 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000757}
758
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000759static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000760mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000761{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000762 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000763}
764
Martin v. Löwis18e16552006-02-15 17:27:45 +0000765static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000766mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000767{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000768 CHECK_VALID(-1);
769 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000770}
771
772static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000773mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000774{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000775 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700776 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000777 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
778 return NULL;
779 }
780 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000781}
782
783static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000784mmap_subscript(mmap_object *self, PyObject *item)
785{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000786 CHECK_VALID(NULL);
787 if (PyIndex_Check(item)) {
788 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
789 if (i == -1 && PyErr_Occurred())
790 return NULL;
791 if (i < 0)
792 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700793 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000794 PyErr_SetString(PyExc_IndexError,
795 "mmap index out of range");
796 return NULL;
797 }
798 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
799 }
800 else if (PySlice_Check(item)) {
801 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000802
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000803 if (PySlice_GetIndicesEx(item, self->size,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000804 &start, &stop, &step, &slicelen) < 0) {
805 return NULL;
806 }
Guido van Rossum98297ee2007-11-06 21:34:58 +0000807
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000808 if (slicelen <= 0)
809 return PyBytes_FromStringAndSize("", 0);
810 else if (step == 1)
811 return PyBytes_FromStringAndSize(self->data + start,
812 slicelen);
813 else {
814 char *result_buf = (char *)PyMem_Malloc(slicelen);
815 Py_ssize_t cur, i;
816 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000817
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000818 if (result_buf == NULL)
819 return PyErr_NoMemory();
820 for (cur = start, i = 0; i < slicelen;
821 cur += step, i++) {
822 result_buf[i] = self->data[cur];
823 }
824 result = PyBytes_FromStringAndSize(result_buf,
825 slicelen);
826 PyMem_Free(result_buf);
827 return result;
828 }
829 }
830 else {
831 PyErr_SetString(PyExc_TypeError,
832 "mmap indices must be integers");
833 return NULL;
834 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000835}
836
837static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000838mmap_concat(mmap_object *self, PyObject *bb)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000839{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000840 CHECK_VALID(NULL);
841 PyErr_SetString(PyExc_SystemError,
842 "mmaps don't support concatenation");
843 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000844}
845
846static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000847mmap_repeat(mmap_object *self, Py_ssize_t n)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000848{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000849 CHECK_VALID(NULL);
850 PyErr_SetString(PyExc_SystemError,
851 "mmaps don't support repeat operation");
852 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000853}
854
855static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000856mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000857{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000858 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000859
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000860 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700861 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000862 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
863 return -1;
864 }
865 if (v == NULL) {
866 PyErr_SetString(PyExc_TypeError,
867 "mmap object doesn't support item deletion");
868 return -1;
869 }
870 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
871 PyErr_SetString(PyExc_IndexError,
872 "mmap assignment must be length-1 bytes()");
873 return -1;
874 }
875 if (!is_writable(self))
876 return -1;
877 buf = PyBytes_AsString(v);
878 self->data[i] = buf[0];
879 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000880}
881
Thomas Woutersed03b412007-08-28 21:37:11 +0000882static int
883mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
884{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000885 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000886
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000887 if (!is_writable(self))
888 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000889
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000890 if (PyIndex_Check(item)) {
891 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
892 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000893
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000894 if (i == -1 && PyErr_Occurred())
895 return -1;
896 if (i < 0)
897 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700898 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000899 PyErr_SetString(PyExc_IndexError,
900 "mmap index out of range");
901 return -1;
902 }
903 if (value == NULL) {
904 PyErr_SetString(PyExc_TypeError,
905 "mmap doesn't support item deletion");
906 return -1;
907 }
908 if (!PyIndex_Check(value)) {
909 PyErr_SetString(PyExc_TypeError,
910 "mmap item value must be an int");
911 return -1;
912 }
913 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
914 if (v == -1 && PyErr_Occurred())
915 return -1;
916 if (v < 0 || v > 255) {
917 PyErr_SetString(PyExc_ValueError,
918 "mmap item value must be "
919 "in range(0, 256)");
920 return -1;
921 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000922 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000923 return 0;
924 }
925 else if (PySlice_Check(item)) {
926 Py_ssize_t start, stop, step, slicelen;
927 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000928
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000929 if (PySlice_GetIndicesEx(item,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000930 self->size, &start, &stop,
931 &step, &slicelen) < 0) {
932 return -1;
933 }
934 if (value == NULL) {
935 PyErr_SetString(PyExc_TypeError,
936 "mmap object doesn't support slice deletion");
937 return -1;
938 }
939 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
940 return -1;
941 if (vbuf.len != slicelen) {
942 PyErr_SetString(PyExc_IndexError,
943 "mmap slice assignment is wrong size");
944 PyBuffer_Release(&vbuf);
945 return -1;
946 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000947
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000948 if (slicelen == 0) {
949 }
950 else if (step == 1) {
951 memcpy(self->data + start, vbuf.buf, slicelen);
952 }
953 else {
954 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000955
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000956 for (cur = start, i = 0;
957 i < slicelen;
958 cur += step, i++)
959 {
960 self->data[cur] = ((char *)vbuf.buf)[i];
961 }
962 }
963 PyBuffer_Release(&vbuf);
964 return 0;
965 }
966 else {
967 PyErr_SetString(PyExc_TypeError,
968 "mmap indices must be integer");
969 return -1;
970 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000971}
972
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000973static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100974 (lenfunc)mmap_length, /*sq_length*/
975 (binaryfunc)mmap_concat, /*sq_concat*/
976 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
977 (ssizeargfunc)mmap_item, /*sq_item*/
978 0, /*sq_slice*/
979 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
980 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000981};
982
Thomas Woutersed03b412007-08-28 21:37:11 +0000983static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000984 (lenfunc)mmap_length,
985 (binaryfunc)mmap_subscript,
986 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000987};
988
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000989static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000990 (getbufferproc)mmap_buffer_getbuf,
991 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000992};
993
Georg Brandl86def6c2008-01-21 20:36:10 +0000994static PyObject *
995new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
996
Christian Heimese1c98112008-01-21 11:20:28 +0000997PyDoc_STRVAR(mmap_doc,
998"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
999\n\
1000Maps length bytes from the file specified by the file handle fileno,\n\
1001and returns a mmap object. If length is larger than the current size\n\
1002of the file, the file is extended to contain length bytes. If length\n\
1003is 0, the maximum length of the map is the current size of the file,\n\
1004except that if the file is empty Windows raises an exception (you cannot\n\
1005create an empty mapping on Windows).\n\
1006\n\
1007Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1008\n\
1009Maps length bytes from the file specified by the file descriptor fileno,\n\
1010and returns a mmap object. If length is 0, the maximum length of the map\n\
1011will be the current size of the file when mmap is called.\n\
1012flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1013private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +00001014object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +00001015that's shared with all other processes mapping the same areas of the file.\n\
1016The default value is MAP_SHARED.\n\
1017\n\
1018To map anonymous memory, pass -1 as the fileno (both versions).");
1019
1020
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001021static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001022 PyVarObject_HEAD_INIT(NULL, 0)
1023 "mmap.mmap", /* tp_name */
1024 sizeof(mmap_object), /* tp_size */
1025 0, /* tp_itemsize */
1026 /* methods */
1027 (destructor) mmap_object_dealloc, /* tp_dealloc */
1028 0, /* tp_print */
1029 0, /* tp_getattr */
1030 0, /* tp_setattr */
1031 0, /* tp_reserved */
1032 0, /* tp_repr */
1033 0, /* tp_as_number */
1034 &mmap_as_sequence, /*tp_as_sequence*/
1035 &mmap_as_mapping, /*tp_as_mapping*/
1036 0, /*tp_hash*/
1037 0, /*tp_call*/
1038 0, /*tp_str*/
1039 PyObject_GenericGetAttr, /*tp_getattro*/
1040 0, /*tp_setattro*/
1041 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001042 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001043 mmap_doc, /*tp_doc*/
1044 0, /* tp_traverse */
1045 0, /* tp_clear */
1046 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001047 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001048 0, /* tp_iter */
1049 0, /* tp_iternext */
1050 mmap_object_methods, /* tp_methods */
1051 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001052 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001053 0, /* tp_base */
1054 0, /* tp_dict */
1055 0, /* tp_descr_get */
1056 0, /* tp_descr_set */
1057 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001058 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001059 PyType_GenericAlloc, /* tp_alloc */
1060 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001061 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001062};
1063
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001064
Tim Petersec0a5f02006-02-16 23:47:20 +00001065#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001066#ifdef HAVE_LARGEFILE_SUPPORT
1067#define _Py_PARSE_OFF_T "L"
1068#else
1069#define _Py_PARSE_OFF_T "l"
1070#endif
1071
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001072static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001073new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001074{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001075 struct _Py_stat_struct status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001076 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001077 Py_ssize_t map_size;
1078 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001079 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1080 int devzero = -1;
1081 int access = (int)ACCESS_DEFAULT;
1082 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001083 "flags", "prot",
1084 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001085
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001086 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1087 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001088 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001089 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001090 if (map_size < 0) {
1091 PyErr_SetString(PyExc_OverflowError,
1092 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001093 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001094 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001095 if (offset < 0) {
1096 PyErr_SetString(PyExc_OverflowError,
1097 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001098 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001099 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001100
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001101 if ((access != (int)ACCESS_DEFAULT) &&
1102 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1103 return PyErr_Format(PyExc_ValueError,
1104 "mmap can't specify both access and flags, prot.");
1105 switch ((access_mode)access) {
1106 case ACCESS_READ:
1107 flags = MAP_SHARED;
1108 prot = PROT_READ;
1109 break;
1110 case ACCESS_WRITE:
1111 flags = MAP_SHARED;
1112 prot = PROT_READ | PROT_WRITE;
1113 break;
1114 case ACCESS_COPY:
1115 flags = MAP_PRIVATE;
1116 prot = PROT_READ | PROT_WRITE;
1117 break;
1118 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001119 /* map prot to access type */
1120 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1121 /* ACCESS_DEFAULT */
1122 }
1123 else if (prot & PROT_WRITE) {
1124 access = ACCESS_WRITE;
1125 }
1126 else {
1127 access = ACCESS_READ;
1128 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001129 break;
1130 default:
1131 return PyErr_Format(PyExc_ValueError,
1132 "mmap invalid access parameter.");
1133 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001134
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001135#ifdef __APPLE__
1136 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1137 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1138 if (fd != -1)
1139 (void)fcntl(fd, F_FULLFSYNC);
1140#endif
Victor Stinnere134a7f2015-03-30 10:09:31 +02001141 if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1142 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001143 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001144 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001145 PyErr_SetString(PyExc_ValueError,
1146 "cannot mmap an empty file");
1147 return NULL;
1148 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001149 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001150 PyErr_SetString(PyExc_ValueError,
1151 "mmap offset is greater than file size");
1152 return NULL;
1153 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001154 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001155 PyErr_SetString(PyExc_ValueError,
1156 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001157 return NULL;
1158 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001159 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001160 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001161 PyErr_SetString(PyExc_ValueError,
1162 "mmap length is greater than file size");
1163 return NULL;
1164 }
1165 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001166 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1167 if (m_obj == NULL) {return NULL;}
1168 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001169 m_obj->size = map_size;
1170 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001171 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001172 m_obj->exports = 0;
1173 m_obj->offset = offset;
1174 if (fd == -1) {
1175 m_obj->fd = -1;
1176 /* Assume the caller wants to map anonymous memory.
1177 This is the same behaviour as Windows. mmap.mmap(-1, size)
1178 on both Windows and Unix map anonymous memory.
1179 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001180#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001181 /* BSD way to map anonymous memory */
1182 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001183#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001184 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001185 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001186 if (devzero == -1) {
1187 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001188 return NULL;
1189 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001190#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001191 }
1192 else {
1193 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001194 if (m_obj->fd == -1) {
1195 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001196 return NULL;
1197 }
1198 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001199
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001200 m_obj->data = mmap(NULL, map_size,
1201 prot, flags,
1202 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001203
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001204 if (devzero != -1) {
1205 close(devzero);
1206 }
1207
1208 if (m_obj->data == (char *)-1) {
1209 m_obj->data = NULL;
1210 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001211 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001212 return NULL;
1213 }
1214 m_obj->access = (access_mode)access;
1215 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001216}
1217#endif /* UNIX */
1218
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001219#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001220
1221/* A note on sizes and offsets: while the actual map size must hold in a
1222 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001223 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001224*/
1225
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001226static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001227new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001228{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001229 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001230 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001231 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001232 DWORD off_hi; /* upper 32 bits of offset */
1233 DWORD off_lo; /* lower 32 bits of offset */
1234 DWORD size_hi; /* upper 32 bits of size */
1235 DWORD size_lo; /* lower 32 bits of size */
1236 char *tagname = "";
1237 DWORD dwErr = 0;
1238 int fileno;
1239 HANDLE fh = 0;
1240 int access = (access_mode)ACCESS_DEFAULT;
1241 DWORD flProtect, dwDesiredAccess;
1242 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001243 "tagname",
1244 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001245
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001246 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1247 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001248 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001249 return NULL;
1250 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001251
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001252 switch((access_mode)access) {
1253 case ACCESS_READ:
1254 flProtect = PAGE_READONLY;
1255 dwDesiredAccess = FILE_MAP_READ;
1256 break;
1257 case ACCESS_DEFAULT: case ACCESS_WRITE:
1258 flProtect = PAGE_READWRITE;
1259 dwDesiredAccess = FILE_MAP_WRITE;
1260 break;
1261 case ACCESS_COPY:
1262 flProtect = PAGE_WRITECOPY;
1263 dwDesiredAccess = FILE_MAP_COPY;
1264 break;
1265 default:
1266 return PyErr_Format(PyExc_ValueError,
1267 "mmap invalid access parameter.");
1268 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001269
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001270 if (map_size < 0) {
1271 PyErr_SetString(PyExc_OverflowError,
1272 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001273 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001274 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001275 if (offset < 0) {
1276 PyErr_SetString(PyExc_OverflowError,
1277 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001278 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001279 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001280
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001281 /* assume -1 and 0 both mean invalid filedescriptor
1282 to 'anonymously' map memory.
1283 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1284 XXX: Should this code be added?
1285 if (fileno == 0)
1286 PyErr_WarnEx(PyExc_DeprecationWarning,
1287 "don't use 0 for anonymous memory",
1288 1);
1289 */
1290 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001291 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001292 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001293 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001294 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001295 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001296 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001297 return NULL;
1298 }
1299 /* Win9x appears to need us seeked to zero */
1300 lseek(fileno, 0, SEEK_SET);
1301 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001302
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001303 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1304 if (m_obj == NULL)
1305 return NULL;
1306 /* Set every field to an invalid marker, so we can safely
1307 destruct the object in the face of failure */
1308 m_obj->data = NULL;
1309 m_obj->file_handle = INVALID_HANDLE_VALUE;
1310 m_obj->map_handle = NULL;
1311 m_obj->tagname = NULL;
1312 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001313
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001314 if (fh) {
1315 /* It is necessary to duplicate the handle, so the
1316 Python code can close it on us */
1317 if (!DuplicateHandle(
1318 GetCurrentProcess(), /* source process handle */
1319 fh, /* handle to be duplicated */
1320 GetCurrentProcess(), /* target proc handle */
1321 (LPHANDLE)&m_obj->file_handle, /* result */
1322 0, /* access - ignored due to options value */
1323 FALSE, /* inherited by child processes? */
1324 DUPLICATE_SAME_ACCESS)) { /* options */
1325 dwErr = GetLastError();
1326 Py_DECREF(m_obj);
1327 PyErr_SetFromWindowsErr(dwErr);
1328 return NULL;
1329 }
1330 if (!map_size) {
1331 DWORD low,high;
1332 low = GetFileSize(fh, &high);
1333 /* low might just happen to have the value INVALID_FILE_SIZE;
1334 so we need to check the last error also. */
1335 if (low == INVALID_FILE_SIZE &&
1336 (dwErr = GetLastError()) != NO_ERROR) {
1337 Py_DECREF(m_obj);
1338 return PyErr_SetFromWindowsErr(dwErr);
1339 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001340
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001341 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001342 if (size == 0) {
1343 PyErr_SetString(PyExc_ValueError,
1344 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001345 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001346 return NULL;
1347 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001348 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001349 PyErr_SetString(PyExc_ValueError,
1350 "mmap offset is greater than file size");
1351 Py_DECREF(m_obj);
1352 return NULL;
1353 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001354 if (size - offset > PY_SSIZE_T_MAX) {
1355 PyErr_SetString(PyExc_ValueError,
1356 "mmap length is too large");
1357 Py_DECREF(m_obj);
1358 return NULL;
1359 }
1360 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001361 } else {
1362 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001363 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001364 }
1365 }
1366 else {
1367 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001368 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001369 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001370
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001371 /* set the initial position */
1372 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001373
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001374 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001375 m_obj->exports = 0;
1376 /* set the tag name */
1377 if (tagname != NULL && *tagname != '\0') {
1378 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1379 if (m_obj->tagname == NULL) {
1380 PyErr_NoMemory();
1381 Py_DECREF(m_obj);
1382 return NULL;
1383 }
1384 strcpy(m_obj->tagname, tagname);
1385 }
1386 else
1387 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001388
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001389 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001390 size_hi = (DWORD)(size >> 32);
1391 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001392 off_hi = (DWORD)(offset >> 32);
1393 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001394 /* For files, it would be sufficient to pass 0 as size.
1395 For anonymous maps, we have to pass the size explicitly. */
1396 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1397 NULL,
1398 flProtect,
1399 size_hi,
1400 size_lo,
1401 m_obj->tagname);
1402 if (m_obj->map_handle != NULL) {
1403 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1404 dwDesiredAccess,
1405 off_hi,
1406 off_lo,
1407 m_obj->size);
1408 if (m_obj->data != NULL)
1409 return (PyObject *)m_obj;
1410 else {
1411 dwErr = GetLastError();
1412 CloseHandle(m_obj->map_handle);
1413 m_obj->map_handle = NULL;
1414 }
1415 } else
1416 dwErr = GetLastError();
1417 Py_DECREF(m_obj);
1418 PyErr_SetFromWindowsErr(dwErr);
1419 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001420}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001421#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001422
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001423static void
1424setint(PyObject *d, const char *name, long value)
1425{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001426 PyObject *o = PyLong_FromLong(value);
1427 if (o && PyDict_SetItemString(d, name, o) == 0) {
1428 Py_DECREF(o);
1429 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001430}
1431
Martin v. Löwis1a214512008-06-11 05:26:20 +00001432
1433static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001434 PyModuleDef_HEAD_INIT,
1435 "mmap",
1436 NULL,
1437 -1,
1438 NULL,
1439 NULL,
1440 NULL,
1441 NULL,
1442 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001443};
1444
Mark Hammond62b1ab12002-07-23 06:31:15 +00001445PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001446PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001447{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001448 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001449
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001450 if (PyType_Ready(&mmap_object_type) < 0)
1451 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001452
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001453 module = PyModule_Create(&mmapmodule);
1454 if (module == NULL)
1455 return NULL;
1456 dict = PyModule_GetDict(module);
1457 if (!dict)
1458 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001459 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001460 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001461#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001462 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001463#endif
1464#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001465 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001466#endif
1467#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001468 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001469#endif
1470
1471#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001472 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001473#endif
1474#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001475 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001476#endif
1477#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001478 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001479#endif
1480#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001481 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001482#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001483#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001484 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1485 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001486#endif
1487
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001488 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001489
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001490 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001491
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001492 setint(dict, "ACCESS_READ", ACCESS_READ);
1493 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1494 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1495 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001496}