blob: 27030db49b24b96a15cd08a85cdf52f17bda8d3e [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
243static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000244mmap_read_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000245 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000246{
Benjamin Peterson8f1cdc62016-10-05 23:32:09 -0700247 Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000248 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000249
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000250 CHECK_VALID(NULL);
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300251 if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000252 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000253
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000254 /* silently 'adjust' out-of-range requests */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700255 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
256 if (num_bytes < 0 || num_bytes > remaining)
257 num_bytes = remaining;
258 result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000259 self->pos += num_bytes;
260 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000261}
262
263static PyObject *
Georg Brandlfceab5a2008-01-19 20:08:23 +0000264mmap_gfind(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000265 PyObject *args,
266 int reverse)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000267{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000268 Py_ssize_t start = self->pos;
269 Py_ssize_t end = self->size;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200270 Py_buffer view;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000271
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000272 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200273 if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
274 &view, &start, &end)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000275 return NULL;
276 } else {
277 const char *p, *start_p, *end_p;
278 int sign = reverse ? -1 : 1;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200279 const char *needle = view.buf;
280 Py_ssize_t len = view.len;
Greg Stein834f4dd2001-05-14 09:32:26 +0000281
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000282 if (start < 0)
283 start += self->size;
284 if (start < 0)
285 start = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700286 else if (start > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000287 start = self->size;
Greg Stein834f4dd2001-05-14 09:32:26 +0000288
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000289 if (end < 0)
290 end += self->size;
291 if (end < 0)
292 end = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700293 else if (end > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000294 end = self->size;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000295
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000296 start_p = self->data + start;
297 end_p = self->data + end;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000298
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000299 for (p = (reverse ? end_p - len : start_p);
300 (p >= start_p) && (p + len <= end_p); p += sign) {
301 Py_ssize_t i;
302 for (i = 0; i < len && needle[i] == p[i]; ++i)
303 /* nothing */;
304 if (i == len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200305 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000306 return PyLong_FromSsize_t(p - self->data);
307 }
308 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200309 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000310 return PyLong_FromLong(-1);
311 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000312}
313
Georg Brandlfceab5a2008-01-19 20:08:23 +0000314static PyObject *
315mmap_find_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000316 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000317{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000318 return mmap_gfind(self, args, 0);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000319}
320
321static PyObject *
322mmap_rfind_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000323 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000324{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000325 return mmap_gfind(self, args, 1);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000326}
327
Tim Petersec0a5f02006-02-16 23:47:20 +0000328static int
Sean Reifscheider54cf12b2007-09-17 17:55:36 +0000329is_writable(mmap_object *self)
Tim Peters5ebfd362001-11-13 23:11:19 +0000330{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000331 if (self->access != ACCESS_READ)
332 return 1;
333 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
334 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000335}
336
Tim Petersec0a5f02006-02-16 23:47:20 +0000337static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000338is_resizeable(mmap_object *self)
339{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000340 if (self->exports > 0) {
341 PyErr_SetString(PyExc_BufferError,
342 "mmap can't resize with extant buffers exported.");
343 return 0;
344 }
345 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
346 return 1;
347 PyErr_Format(PyExc_TypeError,
348 "mmap can't resize a readonly or copy-on-write memory map.");
349 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000350}
351
352
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000353static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000354mmap_write_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000355 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000356{
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200357 Py_buffer data;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000358
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000359 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200360 if (!PyArg_ParseTuple(args, "y*:write", &data))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000361 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000362
Benjamin Peterson37768362016-10-05 23:29:07 -0700363 if (!is_writable(self)) {
364 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700365 return NULL;
Benjamin Peterson37768362016-10-05 23:29:07 -0700366 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700367
368 if (self->pos > self->size || self->size - self->pos < data.len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200369 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700370 PyErr_SetString(PyExc_ValueError, "data out of range");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000371 return NULL;
372 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200373
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700374 memcpy(&self->data[self->pos], data.buf, data.len);
375 self->pos += data.len;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200376 PyBuffer_Release(&data);
Benjamin Peterson87845bc2016-10-05 22:54:19 -0700377 return PyLong_FromSsize_t(data.len);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000378}
379
380static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000381mmap_write_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000382 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000383{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000384 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000385
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000386 CHECK_VALID(NULL);
387 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
388 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000389
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000390 if (!is_writable(self))
391 return NULL;
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000392
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000393 if (self->pos < self->size) {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700394 self->data[self->pos++] = value;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200395 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000396 }
397 else {
398 PyErr_SetString(PyExc_ValueError, "write byte out of range");
399 return NULL;
400 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000401}
Tim Petersec0a5f02006-02-16 23:47:20 +0000402
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000403static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000404mmap_size_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000406{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000407 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000408
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000409#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000410 if (self->file_handle != INVALID_HANDLE_VALUE) {
411 DWORD low,high;
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700412 long long size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000413 low = GetFileSize(self->file_handle, &high);
414 if (low == INVALID_FILE_SIZE) {
415 /* It might be that the function appears to have failed,
416 when indeed its size equals INVALID_FILE_SIZE */
417 DWORD error = GetLastError();
418 if (error != NO_ERROR)
419 return PyErr_SetFromWindowsErr(error);
420 }
421 if (!high && low < LONG_MAX)
422 return PyLong_FromLong((long)low);
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700423 size = (((long long)high)<<32) + low;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000424 return PyLong_FromLongLong(size);
425 } else {
426 return PyLong_FromSsize_t(self->size);
427 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000428#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000429
430#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000431 {
Victor Stinnere134a7f2015-03-30 10:09:31 +0200432 struct _Py_stat_struct status;
433 if (_Py_fstat(self->fd, &status) == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000434 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000435#ifdef HAVE_LARGEFILE_SUPPORT
Victor Stinnere134a7f2015-03-30 10:09:31 +0200436 return PyLong_FromLongLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000437#else
Victor Stinnere134a7f2015-03-30 10:09:31 +0200438 return PyLong_FromLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000439#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000440 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000441#endif /* UNIX */
442}
443
444/* This assumes that you want the entire file mapped,
445 / and when recreating the map will make the new file
446 / have the new size
447 /
448 / Is this really necessary? This could easily be done
449 / from python by just closing and re-opening with the
450 / new size?
451 */
452
453static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000454mmap_resize_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000455 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000456{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000457 Py_ssize_t new_size;
458 CHECK_VALID(NULL);
459 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
460 !is_resizeable(self)) {
461 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700462 }
463 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
464 PyErr_SetString(PyExc_ValueError, "new size out of range");
465 return NULL;
466 }
467
468 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000469#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000470 DWORD dwErrCode = 0;
471 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
472 /* First, unmap the file view */
473 UnmapViewOfFile(self->data);
474 self->data = NULL;
475 /* Close the mapping object */
476 CloseHandle(self->map_handle);
477 self->map_handle = NULL;
478 /* Move to the desired EOF position */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000479 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
480 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
481 off_hi = (DWORD)(self->offset >> 32);
482 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000483 SetFilePointer(self->file_handle,
484 newSizeLow, &newSizeHigh, FILE_BEGIN);
485 /* Change the size of the file */
486 SetEndOfFile(self->file_handle);
487 /* Create another mapping object and remap the file view */
488 self->map_handle = CreateFileMapping(
489 self->file_handle,
490 NULL,
491 PAGE_READWRITE,
492 0,
493 0,
494 self->tagname);
495 if (self->map_handle != NULL) {
496 self->data = (char *) MapViewOfFile(self->map_handle,
497 FILE_MAP_WRITE,
498 off_hi,
499 off_lo,
500 new_size);
501 if (self->data != NULL) {
502 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200503 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000504 } else {
505 dwErrCode = GetLastError();
506 CloseHandle(self->map_handle);
507 self->map_handle = NULL;
508 }
509 } else {
510 dwErrCode = GetLastError();
511 }
512 PyErr_SetFromWindowsErr(dwErrCode);
513 return NULL;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000514#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000515
516#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000517#ifndef HAVE_MREMAP
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000518 PyErr_SetString(PyExc_SystemError,
519 "mmap: resizing not available--no mremap()");
520 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000521#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000522 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000523
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700524 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200525 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000526 return NULL;
527 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000528
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000529#ifdef MREMAP_MAYMOVE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000530 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000531#else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700532#if defined(__NetBSD__)
533 newmap = mremap(self->data, self->size, self->data, new_size, 0);
534#else
535 newmap = mremap(self->data, self->size, new_size, 0);
536#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000537#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000538 if (newmap == (void *)-1)
539 {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200540 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000541 return NULL;
542 }
543 self->data = newmap;
544 self->size = new_size;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200545 Py_RETURN_NONE;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000546#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000547#endif /* UNIX */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000548 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000549}
550
551static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000552mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000553{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000554 CHECK_VALID(NULL);
555 return PyLong_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000556}
557
558static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000559mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000560{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000561 Py_ssize_t offset = 0;
562 Py_ssize_t size = self->size;
563 CHECK_VALID(NULL);
564 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
565 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700566 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000567 PyErr_SetString(PyExc_ValueError, "flush values out of range");
568 return NULL;
569 }
R. David Murraye194dd62010-10-18 01:14:06 +0000570
571 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
572 return PyLong_FromLong(0);
573
Christian Heimesaf98da12008-01-27 15:18:18 +0000574#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000575 return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
Christian Heimesaf98da12008-01-27 15:18:18 +0000576#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000577 /* XXX semantics of return value? */
578 /* XXX flags for msync? */
579 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200580 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000581 return NULL;
582 }
583 return PyLong_FromLong(0);
Christian Heimesaf98da12008-01-27 15:18:18 +0000584#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000585 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
586 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000587#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000588}
589
590static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000591mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000592{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000593 Py_ssize_t dist;
594 int how=0;
595 CHECK_VALID(NULL);
596 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
597 return NULL;
598 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700599 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000600 switch (how) {
601 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000602 where = dist;
603 break;
604 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700605 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000606 goto onoutofrange;
607 where = self->pos + dist;
608 break;
609 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700610 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000611 goto onoutofrange;
612 where = self->size + dist;
613 break;
614 default:
615 PyErr_SetString(PyExc_ValueError, "unknown seek type");
616 return NULL;
617 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700618 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000619 goto onoutofrange;
620 self->pos = where;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200621 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000622 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000623
Tim Peters5ebfd362001-11-13 23:11:19 +0000624 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000625 PyErr_SetString(PyExc_ValueError, "seek out of range");
626 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000627}
628
629static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000630mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000631{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700632 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000633 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700634 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000635 !is_writable(self)) {
636 return NULL;
637 } else {
638 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700639 if (dest < 0 || src < 0 || cnt < 0)
640 goto bounds;
641 if (self->size - dest < cnt || self->size - src < cnt)
642 goto bounds;
643
644 memmove(&self->data[dest], &self->data[src], cnt);
645
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200646 Py_RETURN_NONE;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700647
648 bounds:
649 PyErr_SetString(PyExc_ValueError,
650 "source, destination, or count out of range");
651 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000652 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000653}
654
Georg Brandl0bccc182010-08-01 14:50:00 +0000655static PyObject *
656mmap_closed_get(mmap_object *self)
657{
658#ifdef MS_WINDOWS
659 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
660#elif defined(UNIX)
661 return PyBool_FromLong(self->data == NULL ? 1 : 0);
662#endif
663}
664
665static PyObject *
666mmap__enter__method(mmap_object *self, PyObject *args)
667{
668 CHECK_VALID(NULL);
669
670 Py_INCREF(self);
671 return (PyObject *)self;
672}
673
674static PyObject *
675mmap__exit__method(PyObject *self, PyObject *args)
676{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200677 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200678
679 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000680}
681
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300682#ifdef MS_WINDOWS
683static PyObject *
684mmap__sizeof__method(mmap_object *self, void *unused)
685{
686 Py_ssize_t res;
687
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200688 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300689 if (self->tagname)
690 res += strlen(self->tagname) + 1;
691 return PyLong_FromSsize_t(res);
692}
693#endif
694
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000695static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000696 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
697 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
698 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
699 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
700 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
701 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
702 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
703 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
704 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
705 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
706 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
707 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
708 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
709 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000710 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
711 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300712#ifdef MS_WINDOWS
713 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
714#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000715 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000716};
717
Georg Brandl0bccc182010-08-01 14:50:00 +0000718static PyGetSetDef mmap_object_getset[] = {
719 {"closed", (getter) mmap_closed_get, NULL, NULL},
720 {NULL}
721};
722
723
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000724/* Functions for treating an mmap'ed file as a buffer */
725
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000726static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000727mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000728{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000729 CHECK_VALID(-1);
730 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
731 (self->access == ACCESS_READ), flags) < 0)
732 return -1;
733 self->exports++;
734 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000735}
736
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000737static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000738mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000739{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000740 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000741}
742
Martin v. Löwis18e16552006-02-15 17:27:45 +0000743static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000744mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000745{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000746 CHECK_VALID(-1);
747 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000748}
749
750static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000751mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000752{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000753 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700754 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000755 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
756 return NULL;
757 }
758 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000759}
760
761static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000762mmap_subscript(mmap_object *self, PyObject *item)
763{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000764 CHECK_VALID(NULL);
765 if (PyIndex_Check(item)) {
766 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
767 if (i == -1 && PyErr_Occurred())
768 return NULL;
769 if (i < 0)
770 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700771 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000772 PyErr_SetString(PyExc_IndexError,
773 "mmap index out of range");
774 return NULL;
775 }
776 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
777 }
778 else if (PySlice_Check(item)) {
779 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000780
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300781 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000782 return NULL;
783 }
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300784 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
Guido van Rossum98297ee2007-11-06 21:34:58 +0000785
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000786 if (slicelen <= 0)
787 return PyBytes_FromStringAndSize("", 0);
788 else if (step == 1)
789 return PyBytes_FromStringAndSize(self->data + start,
790 slicelen);
791 else {
792 char *result_buf = (char *)PyMem_Malloc(slicelen);
793 Py_ssize_t cur, i;
794 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000795
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000796 if (result_buf == NULL)
797 return PyErr_NoMemory();
798 for (cur = start, i = 0; i < slicelen;
799 cur += step, i++) {
800 result_buf[i] = self->data[cur];
801 }
802 result = PyBytes_FromStringAndSize(result_buf,
803 slicelen);
804 PyMem_Free(result_buf);
805 return result;
806 }
807 }
808 else {
809 PyErr_SetString(PyExc_TypeError,
810 "mmap indices must be integers");
811 return NULL;
812 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000813}
814
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000815static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000816mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000817{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000818 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000819
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000820 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700821 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000822 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
823 return -1;
824 }
825 if (v == NULL) {
826 PyErr_SetString(PyExc_TypeError,
827 "mmap object doesn't support item deletion");
828 return -1;
829 }
830 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
831 PyErr_SetString(PyExc_IndexError,
832 "mmap assignment must be length-1 bytes()");
833 return -1;
834 }
835 if (!is_writable(self))
836 return -1;
837 buf = PyBytes_AsString(v);
838 self->data[i] = buf[0];
839 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000840}
841
Thomas Woutersed03b412007-08-28 21:37:11 +0000842static int
843mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
844{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000845 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000846
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000847 if (!is_writable(self))
848 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000849
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000850 if (PyIndex_Check(item)) {
851 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
852 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000853
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000854 if (i == -1 && PyErr_Occurred())
855 return -1;
856 if (i < 0)
857 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700858 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000859 PyErr_SetString(PyExc_IndexError,
860 "mmap index out of range");
861 return -1;
862 }
863 if (value == NULL) {
864 PyErr_SetString(PyExc_TypeError,
865 "mmap doesn't support item deletion");
866 return -1;
867 }
868 if (!PyIndex_Check(value)) {
869 PyErr_SetString(PyExc_TypeError,
870 "mmap item value must be an int");
871 return -1;
872 }
873 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
874 if (v == -1 && PyErr_Occurred())
875 return -1;
876 if (v < 0 || v > 255) {
877 PyErr_SetString(PyExc_ValueError,
878 "mmap item value must be "
879 "in range(0, 256)");
880 return -1;
881 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000882 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000883 return 0;
884 }
885 else if (PySlice_Check(item)) {
886 Py_ssize_t start, stop, step, slicelen;
887 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000888
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300889 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000890 return -1;
891 }
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300892 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000893 if (value == NULL) {
894 PyErr_SetString(PyExc_TypeError,
895 "mmap object doesn't support slice deletion");
896 return -1;
897 }
898 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
899 return -1;
900 if (vbuf.len != slicelen) {
901 PyErr_SetString(PyExc_IndexError,
902 "mmap slice assignment is wrong size");
903 PyBuffer_Release(&vbuf);
904 return -1;
905 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000906
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000907 if (slicelen == 0) {
908 }
909 else if (step == 1) {
910 memcpy(self->data + start, vbuf.buf, slicelen);
911 }
912 else {
913 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000914
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000915 for (cur = start, i = 0;
916 i < slicelen;
917 cur += step, i++)
918 {
919 self->data[cur] = ((char *)vbuf.buf)[i];
920 }
921 }
922 PyBuffer_Release(&vbuf);
923 return 0;
924 }
925 else {
926 PyErr_SetString(PyExc_TypeError,
927 "mmap indices must be integer");
928 return -1;
929 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000930}
931
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000932static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100933 (lenfunc)mmap_length, /*sq_length*/
Zackery Spytze9e39762018-06-05 06:59:41 -0600934 0, /*sq_concat*/
935 0, /*sq_repeat*/
Stefan Krah23186992012-03-06 15:37:36 +0100936 (ssizeargfunc)mmap_item, /*sq_item*/
937 0, /*sq_slice*/
938 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
939 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000940};
941
Thomas Woutersed03b412007-08-28 21:37:11 +0000942static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000943 (lenfunc)mmap_length,
944 (binaryfunc)mmap_subscript,
945 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000946};
947
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000948static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000949 (getbufferproc)mmap_buffer_getbuf,
950 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000951};
952
Georg Brandl86def6c2008-01-21 20:36:10 +0000953static PyObject *
954new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
955
Christian Heimese1c98112008-01-21 11:20:28 +0000956PyDoc_STRVAR(mmap_doc,
957"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
958\n\
959Maps length bytes from the file specified by the file handle fileno,\n\
960and returns a mmap object. If length is larger than the current size\n\
961of the file, the file is extended to contain length bytes. If length\n\
962is 0, the maximum length of the map is the current size of the file,\n\
963except that if the file is empty Windows raises an exception (you cannot\n\
964create an empty mapping on Windows).\n\
965\n\
966Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
967\n\
968Maps length bytes from the file specified by the file descriptor fileno,\n\
969and returns a mmap object. If length is 0, the maximum length of the map\n\
970will be the current size of the file when mmap is called.\n\
971flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
972private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +0000973object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +0000974that's shared with all other processes mapping the same areas of the file.\n\
975The default value is MAP_SHARED.\n\
976\n\
977To map anonymous memory, pass -1 as the fileno (both versions).");
978
979
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000980static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000981 PyVarObject_HEAD_INIT(NULL, 0)
982 "mmap.mmap", /* tp_name */
983 sizeof(mmap_object), /* tp_size */
984 0, /* tp_itemsize */
985 /* methods */
986 (destructor) mmap_object_dealloc, /* tp_dealloc */
987 0, /* tp_print */
988 0, /* tp_getattr */
989 0, /* tp_setattr */
990 0, /* tp_reserved */
991 0, /* tp_repr */
992 0, /* tp_as_number */
993 &mmap_as_sequence, /*tp_as_sequence*/
994 &mmap_as_mapping, /*tp_as_mapping*/
995 0, /*tp_hash*/
996 0, /*tp_call*/
997 0, /*tp_str*/
998 PyObject_GenericGetAttr, /*tp_getattro*/
999 0, /*tp_setattro*/
1000 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001001 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001002 mmap_doc, /*tp_doc*/
1003 0, /* tp_traverse */
1004 0, /* tp_clear */
1005 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001006 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001007 0, /* tp_iter */
1008 0, /* tp_iternext */
1009 mmap_object_methods, /* tp_methods */
1010 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001011 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001012 0, /* tp_base */
1013 0, /* tp_dict */
1014 0, /* tp_descr_get */
1015 0, /* tp_descr_set */
1016 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001017 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001018 PyType_GenericAlloc, /* tp_alloc */
1019 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001020 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001021};
1022
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001023
Tim Petersec0a5f02006-02-16 23:47:20 +00001024#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001025#ifdef HAVE_LARGEFILE_SUPPORT
1026#define _Py_PARSE_OFF_T "L"
1027#else
1028#define _Py_PARSE_OFF_T "l"
1029#endif
1030
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001031static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001032new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001033{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001034 struct _Py_stat_struct status;
Zackery Spytzd6e14042018-03-14 14:08:01 -06001035 int fstat_result = -1;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001036 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001037 Py_ssize_t map_size;
1038 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001039 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1040 int devzero = -1;
1041 int access = (int)ACCESS_DEFAULT;
1042 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001043 "flags", "prot",
1044 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001045
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001046 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1047 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001048 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001049 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001050 if (map_size < 0) {
1051 PyErr_SetString(PyExc_OverflowError,
Zackery Spytz9308dea2018-03-21 00:02:37 -06001052 "memory mapped length must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001053 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001054 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001055 if (offset < 0) {
1056 PyErr_SetString(PyExc_OverflowError,
1057 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001058 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001059 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001060
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001061 if ((access != (int)ACCESS_DEFAULT) &&
1062 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1063 return PyErr_Format(PyExc_ValueError,
1064 "mmap can't specify both access and flags, prot.");
1065 switch ((access_mode)access) {
1066 case ACCESS_READ:
1067 flags = MAP_SHARED;
1068 prot = PROT_READ;
1069 break;
1070 case ACCESS_WRITE:
1071 flags = MAP_SHARED;
1072 prot = PROT_READ | PROT_WRITE;
1073 break;
1074 case ACCESS_COPY:
1075 flags = MAP_PRIVATE;
1076 prot = PROT_READ | PROT_WRITE;
1077 break;
1078 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001079 /* map prot to access type */
1080 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1081 /* ACCESS_DEFAULT */
1082 }
1083 else if (prot & PROT_WRITE) {
1084 access = ACCESS_WRITE;
1085 }
1086 else {
1087 access = ACCESS_READ;
1088 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001089 break;
1090 default:
1091 return PyErr_Format(PyExc_ValueError,
1092 "mmap invalid access parameter.");
1093 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001094
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001095#ifdef __APPLE__
1096 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1097 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1098 if (fd != -1)
1099 (void)fcntl(fd, F_FULLFSYNC);
1100#endif
Nir Soffer4484f9d2018-03-12 01:39:22 +02001101
1102 if (fd != -1) {
1103 Py_BEGIN_ALLOW_THREADS
1104 fstat_result = _Py_fstat_noraise(fd, &status);
1105 Py_END_ALLOW_THREADS
1106 }
1107
1108 if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001109 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001110 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001111 PyErr_SetString(PyExc_ValueError,
1112 "cannot mmap an empty file");
1113 return NULL;
1114 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001115 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001116 PyErr_SetString(PyExc_ValueError,
1117 "mmap offset is greater than file size");
1118 return NULL;
1119 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001120 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001121 PyErr_SetString(PyExc_ValueError,
1122 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001123 return NULL;
1124 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001125 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001126 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001127 PyErr_SetString(PyExc_ValueError,
1128 "mmap length is greater than file size");
1129 return NULL;
1130 }
1131 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001132 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1133 if (m_obj == NULL) {return NULL;}
1134 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001135 m_obj->size = map_size;
1136 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001137 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001138 m_obj->exports = 0;
1139 m_obj->offset = offset;
1140 if (fd == -1) {
1141 m_obj->fd = -1;
1142 /* Assume the caller wants to map anonymous memory.
1143 This is the same behaviour as Windows. mmap.mmap(-1, size)
1144 on both Windows and Unix map anonymous memory.
1145 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001146#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001147 /* BSD way to map anonymous memory */
1148 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001149#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001150 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001151 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001152 if (devzero == -1) {
1153 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001154 return NULL;
1155 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001156#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001157 }
1158 else {
1159 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001160 if (m_obj->fd == -1) {
1161 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001162 return NULL;
1163 }
1164 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001165
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001166 m_obj->data = mmap(NULL, map_size,
1167 prot, flags,
1168 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001169
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001170 if (devzero != -1) {
1171 close(devzero);
1172 }
1173
1174 if (m_obj->data == (char *)-1) {
1175 m_obj->data = NULL;
1176 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001177 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001178 return NULL;
1179 }
1180 m_obj->access = (access_mode)access;
1181 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001182}
1183#endif /* UNIX */
1184
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001185#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001186
1187/* A note on sizes and offsets: while the actual map size must hold in a
1188 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001189 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001190*/
1191
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001192static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001193new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001194{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001195 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001196 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001197 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001198 DWORD off_hi; /* upper 32 bits of offset */
1199 DWORD off_lo; /* lower 32 bits of offset */
1200 DWORD size_hi; /* upper 32 bits of size */
1201 DWORD size_lo; /* lower 32 bits of size */
Serhiy Storchakae2f92de2017-11-11 13:06:26 +02001202 const char *tagname = "";
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001203 DWORD dwErr = 0;
1204 int fileno;
1205 HANDLE fh = 0;
1206 int access = (access_mode)ACCESS_DEFAULT;
1207 DWORD flProtect, dwDesiredAccess;
1208 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001209 "tagname",
1210 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001211
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001212 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1213 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001214 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001215 return NULL;
1216 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001217
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001218 switch((access_mode)access) {
1219 case ACCESS_READ:
1220 flProtect = PAGE_READONLY;
1221 dwDesiredAccess = FILE_MAP_READ;
1222 break;
1223 case ACCESS_DEFAULT: case ACCESS_WRITE:
1224 flProtect = PAGE_READWRITE;
1225 dwDesiredAccess = FILE_MAP_WRITE;
1226 break;
1227 case ACCESS_COPY:
1228 flProtect = PAGE_WRITECOPY;
1229 dwDesiredAccess = FILE_MAP_COPY;
1230 break;
1231 default:
1232 return PyErr_Format(PyExc_ValueError,
1233 "mmap invalid access parameter.");
1234 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001235
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001236 if (map_size < 0) {
1237 PyErr_SetString(PyExc_OverflowError,
Zackery Spytz9308dea2018-03-21 00:02:37 -06001238 "memory mapped length must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001239 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001240 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001241 if (offset < 0) {
1242 PyErr_SetString(PyExc_OverflowError,
1243 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001244 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001245 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001246
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001247 /* assume -1 and 0 both mean invalid filedescriptor
1248 to 'anonymously' map memory.
1249 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1250 XXX: Should this code be added?
1251 if (fileno == 0)
1252 PyErr_WarnEx(PyExc_DeprecationWarning,
1253 "don't use 0 for anonymous memory",
1254 1);
1255 */
1256 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001257 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001258 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001259 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001260 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001261 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001262 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001263 return NULL;
1264 }
1265 /* Win9x appears to need us seeked to zero */
1266 lseek(fileno, 0, SEEK_SET);
1267 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001268
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001269 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1270 if (m_obj == NULL)
1271 return NULL;
1272 /* Set every field to an invalid marker, so we can safely
1273 destruct the object in the face of failure */
1274 m_obj->data = NULL;
1275 m_obj->file_handle = INVALID_HANDLE_VALUE;
1276 m_obj->map_handle = NULL;
1277 m_obj->tagname = NULL;
1278 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001279
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001280 if (fh) {
1281 /* It is necessary to duplicate the handle, so the
1282 Python code can close it on us */
1283 if (!DuplicateHandle(
1284 GetCurrentProcess(), /* source process handle */
1285 fh, /* handle to be duplicated */
1286 GetCurrentProcess(), /* target proc handle */
1287 (LPHANDLE)&m_obj->file_handle, /* result */
1288 0, /* access - ignored due to options value */
1289 FALSE, /* inherited by child processes? */
1290 DUPLICATE_SAME_ACCESS)) { /* options */
1291 dwErr = GetLastError();
1292 Py_DECREF(m_obj);
1293 PyErr_SetFromWindowsErr(dwErr);
1294 return NULL;
1295 }
1296 if (!map_size) {
1297 DWORD low,high;
1298 low = GetFileSize(fh, &high);
1299 /* low might just happen to have the value INVALID_FILE_SIZE;
1300 so we need to check the last error also. */
1301 if (low == INVALID_FILE_SIZE &&
1302 (dwErr = GetLastError()) != NO_ERROR) {
1303 Py_DECREF(m_obj);
1304 return PyErr_SetFromWindowsErr(dwErr);
1305 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001306
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001307 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001308 if (size == 0) {
1309 PyErr_SetString(PyExc_ValueError,
1310 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001311 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001312 return NULL;
1313 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001314 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001315 PyErr_SetString(PyExc_ValueError,
1316 "mmap offset is greater than file size");
1317 Py_DECREF(m_obj);
1318 return NULL;
1319 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001320 if (size - offset > PY_SSIZE_T_MAX) {
1321 PyErr_SetString(PyExc_ValueError,
1322 "mmap length is too large");
1323 Py_DECREF(m_obj);
1324 return NULL;
1325 }
1326 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001327 } else {
1328 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001329 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001330 }
1331 }
1332 else {
1333 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001334 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001335 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001336
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001337 /* set the initial position */
1338 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001339
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001340 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001341 m_obj->exports = 0;
1342 /* set the tag name */
1343 if (tagname != NULL && *tagname != '\0') {
1344 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1345 if (m_obj->tagname == NULL) {
1346 PyErr_NoMemory();
1347 Py_DECREF(m_obj);
1348 return NULL;
1349 }
1350 strcpy(m_obj->tagname, tagname);
1351 }
1352 else
1353 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001354
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001355 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001356 size_hi = (DWORD)(size >> 32);
1357 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001358 off_hi = (DWORD)(offset >> 32);
1359 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001360 /* For files, it would be sufficient to pass 0 as size.
1361 For anonymous maps, we have to pass the size explicitly. */
1362 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1363 NULL,
1364 flProtect,
1365 size_hi,
1366 size_lo,
1367 m_obj->tagname);
1368 if (m_obj->map_handle != NULL) {
1369 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1370 dwDesiredAccess,
1371 off_hi,
1372 off_lo,
1373 m_obj->size);
1374 if (m_obj->data != NULL)
1375 return (PyObject *)m_obj;
1376 else {
1377 dwErr = GetLastError();
1378 CloseHandle(m_obj->map_handle);
1379 m_obj->map_handle = NULL;
1380 }
1381 } else
1382 dwErr = GetLastError();
1383 Py_DECREF(m_obj);
1384 PyErr_SetFromWindowsErr(dwErr);
1385 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001386}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001387#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001388
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001389static void
1390setint(PyObject *d, const char *name, long value)
1391{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001392 PyObject *o = PyLong_FromLong(value);
1393 if (o && PyDict_SetItemString(d, name, o) == 0) {
1394 Py_DECREF(o);
1395 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001396}
1397
Martin v. Löwis1a214512008-06-11 05:26:20 +00001398
1399static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001400 PyModuleDef_HEAD_INIT,
1401 "mmap",
1402 NULL,
1403 -1,
1404 NULL,
1405 NULL,
1406 NULL,
1407 NULL,
1408 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001409};
1410
Mark Hammond62b1ab12002-07-23 06:31:15 +00001411PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001412PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001413{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001414 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001415
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001416 if (PyType_Ready(&mmap_object_type) < 0)
1417 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001418
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001419 module = PyModule_Create(&mmapmodule);
1420 if (module == NULL)
1421 return NULL;
1422 dict = PyModule_GetDict(module);
1423 if (!dict)
1424 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001425 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001426 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001427#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001428 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001429#endif
1430#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001431 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001432#endif
1433#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001434 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001435#endif
1436
1437#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001438 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001439#endif
1440#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001441 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001442#endif
1443#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001444 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001445#endif
1446#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001447 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001448#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001449#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001450 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1451 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001452#endif
1453
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001454 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001455
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001456 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001457
Justus Schwabedal5a8a84b2017-11-07 15:51:43 -05001458 setint(dict, "ACCESS_DEFAULT", ACCESS_DEFAULT);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001459 setint(dict, "ACCESS_READ", ACCESS_READ);
1460 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1461 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1462 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001463}