blob: af9cd7e2be8cc57ab576d13728d6b92b64912405 [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)
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300572 Py_RETURN_NONE;
R. David Murraye194dd62010-10-18 01:14:06 +0000573
Christian Heimesaf98da12008-01-27 15:18:18 +0000574#ifdef MS_WINDOWS
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300575 if (!FlushViewOfFile(self->data+offset, size)) {
576 PyErr_SetFromWindowsErr(GetLastError());
577 return NULL;
578 }
579 Py_RETURN_NONE;
Christian Heimesaf98da12008-01-27 15:18:18 +0000580#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000581 /* XXX flags for msync? */
582 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200583 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000584 return NULL;
585 }
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300586 Py_RETURN_NONE;
Christian Heimesaf98da12008-01-27 15:18:18 +0000587#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000588 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
589 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000590#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000591}
592
593static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000594mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000595{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000596 Py_ssize_t dist;
597 int how=0;
598 CHECK_VALID(NULL);
599 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
600 return NULL;
601 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700602 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000603 switch (how) {
604 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000605 where = dist;
606 break;
607 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700608 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000609 goto onoutofrange;
610 where = self->pos + dist;
611 break;
612 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700613 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000614 goto onoutofrange;
615 where = self->size + dist;
616 break;
617 default:
618 PyErr_SetString(PyExc_ValueError, "unknown seek type");
619 return NULL;
620 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700621 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000622 goto onoutofrange;
623 self->pos = where;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200624 Py_RETURN_NONE;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000625 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000626
Tim Peters5ebfd362001-11-13 23:11:19 +0000627 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000628 PyErr_SetString(PyExc_ValueError, "seek out of range");
629 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000630}
631
632static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000633mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000634{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700635 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000636 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700637 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000638 !is_writable(self)) {
639 return NULL;
640 } else {
641 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700642 if (dest < 0 || src < 0 || cnt < 0)
643 goto bounds;
644 if (self->size - dest < cnt || self->size - src < cnt)
645 goto bounds;
646
647 memmove(&self->data[dest], &self->data[src], cnt);
648
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200649 Py_RETURN_NONE;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700650
651 bounds:
652 PyErr_SetString(PyExc_ValueError,
653 "source, destination, or count out of range");
654 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000655 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000656}
657
Georg Brandl0bccc182010-08-01 14:50:00 +0000658static PyObject *
659mmap_closed_get(mmap_object *self)
660{
661#ifdef MS_WINDOWS
662 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
663#elif defined(UNIX)
664 return PyBool_FromLong(self->data == NULL ? 1 : 0);
665#endif
666}
667
668static PyObject *
669mmap__enter__method(mmap_object *self, PyObject *args)
670{
671 CHECK_VALID(NULL);
672
673 Py_INCREF(self);
674 return (PyObject *)self;
675}
676
677static PyObject *
678mmap__exit__method(PyObject *self, PyObject *args)
679{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200680 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200681
682 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000683}
684
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300685#ifdef MS_WINDOWS
686static PyObject *
687mmap__sizeof__method(mmap_object *self, void *unused)
688{
689 Py_ssize_t res;
690
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200691 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300692 if (self->tagname)
693 res += strlen(self->tagname) + 1;
694 return PyLong_FromSsize_t(res);
695}
696#endif
697
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000698static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000699 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
700 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
701 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
702 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
703 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
704 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
705 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
706 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
707 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
708 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
709 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
710 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
711 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
712 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000713 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
714 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300715#ifdef MS_WINDOWS
716 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
717#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000718 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000719};
720
Georg Brandl0bccc182010-08-01 14:50:00 +0000721static PyGetSetDef mmap_object_getset[] = {
722 {"closed", (getter) mmap_closed_get, NULL, NULL},
723 {NULL}
724};
725
726
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000727/* Functions for treating an mmap'ed file as a buffer */
728
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000729static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000730mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000731{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000732 CHECK_VALID(-1);
733 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
734 (self->access == ACCESS_READ), flags) < 0)
735 return -1;
736 self->exports++;
737 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000738}
739
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000740static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000741mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000742{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000743 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000744}
745
Martin v. Löwis18e16552006-02-15 17:27:45 +0000746static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000747mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000748{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000749 CHECK_VALID(-1);
750 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000751}
752
753static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000754mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000755{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000756 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700757 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000758 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
759 return NULL;
760 }
761 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000762}
763
764static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000765mmap_subscript(mmap_object *self, PyObject *item)
766{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000767 CHECK_VALID(NULL);
768 if (PyIndex_Check(item)) {
769 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
770 if (i == -1 && PyErr_Occurred())
771 return NULL;
772 if (i < 0)
773 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700774 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000775 PyErr_SetString(PyExc_IndexError,
776 "mmap index out of range");
777 return NULL;
778 }
779 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
780 }
781 else if (PySlice_Check(item)) {
782 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000783
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300784 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000785 return NULL;
786 }
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300787 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
Guido van Rossum98297ee2007-11-06 21:34:58 +0000788
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000789 if (slicelen <= 0)
790 return PyBytes_FromStringAndSize("", 0);
791 else if (step == 1)
792 return PyBytes_FromStringAndSize(self->data + start,
793 slicelen);
794 else {
795 char *result_buf = (char *)PyMem_Malloc(slicelen);
796 Py_ssize_t cur, i;
797 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000798
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000799 if (result_buf == NULL)
800 return PyErr_NoMemory();
801 for (cur = start, i = 0; i < slicelen;
802 cur += step, i++) {
803 result_buf[i] = self->data[cur];
804 }
805 result = PyBytes_FromStringAndSize(result_buf,
806 slicelen);
807 PyMem_Free(result_buf);
808 return result;
809 }
810 }
811 else {
812 PyErr_SetString(PyExc_TypeError,
813 "mmap indices must be integers");
814 return NULL;
815 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000816}
817
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000818static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000819mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000820{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000821 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000822
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000823 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700824 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000825 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
826 return -1;
827 }
828 if (v == NULL) {
829 PyErr_SetString(PyExc_TypeError,
830 "mmap object doesn't support item deletion");
831 return -1;
832 }
833 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
834 PyErr_SetString(PyExc_IndexError,
835 "mmap assignment must be length-1 bytes()");
836 return -1;
837 }
838 if (!is_writable(self))
839 return -1;
840 buf = PyBytes_AsString(v);
841 self->data[i] = buf[0];
842 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000843}
844
Thomas Woutersed03b412007-08-28 21:37:11 +0000845static int
846mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
847{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000848 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000849
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000850 if (!is_writable(self))
851 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000852
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000853 if (PyIndex_Check(item)) {
854 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
855 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000856
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000857 if (i == -1 && PyErr_Occurred())
858 return -1;
859 if (i < 0)
860 i += self->size;
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,
863 "mmap index out of range");
864 return -1;
865 }
866 if (value == NULL) {
867 PyErr_SetString(PyExc_TypeError,
868 "mmap doesn't support item deletion");
869 return -1;
870 }
871 if (!PyIndex_Check(value)) {
872 PyErr_SetString(PyExc_TypeError,
873 "mmap item value must be an int");
874 return -1;
875 }
876 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
877 if (v == -1 && PyErr_Occurred())
878 return -1;
879 if (v < 0 || v > 255) {
880 PyErr_SetString(PyExc_ValueError,
881 "mmap item value must be "
882 "in range(0, 256)");
883 return -1;
884 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000885 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000886 return 0;
887 }
888 else if (PySlice_Check(item)) {
889 Py_ssize_t start, stop, step, slicelen;
890 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000891
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300892 if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000893 return -1;
894 }
Serhiy Storchakab879fe82017-04-08 09:53:51 +0300895 slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000896 if (value == NULL) {
897 PyErr_SetString(PyExc_TypeError,
898 "mmap object doesn't support slice deletion");
899 return -1;
900 }
901 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
902 return -1;
903 if (vbuf.len != slicelen) {
904 PyErr_SetString(PyExc_IndexError,
905 "mmap slice assignment is wrong size");
906 PyBuffer_Release(&vbuf);
907 return -1;
908 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000909
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000910 if (slicelen == 0) {
911 }
912 else if (step == 1) {
913 memcpy(self->data + start, vbuf.buf, slicelen);
914 }
915 else {
916 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000917
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000918 for (cur = start, i = 0;
919 i < slicelen;
920 cur += step, i++)
921 {
922 self->data[cur] = ((char *)vbuf.buf)[i];
923 }
924 }
925 PyBuffer_Release(&vbuf);
926 return 0;
927 }
928 else {
929 PyErr_SetString(PyExc_TypeError,
930 "mmap indices must be integer");
931 return -1;
932 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000933}
934
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000935static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100936 (lenfunc)mmap_length, /*sq_length*/
Zackery Spytze9e39762018-06-05 06:59:41 -0600937 0, /*sq_concat*/
938 0, /*sq_repeat*/
Stefan Krah23186992012-03-06 15:37:36 +0100939 (ssizeargfunc)mmap_item, /*sq_item*/
940 0, /*sq_slice*/
941 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
942 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000943};
944
Thomas Woutersed03b412007-08-28 21:37:11 +0000945static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000946 (lenfunc)mmap_length,
947 (binaryfunc)mmap_subscript,
948 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000949};
950
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000951static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000952 (getbufferproc)mmap_buffer_getbuf,
953 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000954};
955
Georg Brandl86def6c2008-01-21 20:36:10 +0000956static PyObject *
957new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
958
Christian Heimese1c98112008-01-21 11:20:28 +0000959PyDoc_STRVAR(mmap_doc,
960"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
961\n\
962Maps length bytes from the file specified by the file handle fileno,\n\
963and returns a mmap object. If length is larger than the current size\n\
964of the file, the file is extended to contain length bytes. If length\n\
965is 0, the maximum length of the map is the current size of the file,\n\
966except that if the file is empty Windows raises an exception (you cannot\n\
967create an empty mapping on Windows).\n\
968\n\
969Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
970\n\
971Maps length bytes from the file specified by the file descriptor fileno,\n\
972and returns a mmap object. If length is 0, the maximum length of the map\n\
973will be the current size of the file when mmap is called.\n\
974flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
975private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +0000976object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +0000977that's shared with all other processes mapping the same areas of the file.\n\
978The default value is MAP_SHARED.\n\
979\n\
980To map anonymous memory, pass -1 as the fileno (both versions).");
981
982
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000983static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000984 PyVarObject_HEAD_INIT(NULL, 0)
985 "mmap.mmap", /* tp_name */
986 sizeof(mmap_object), /* tp_size */
987 0, /* tp_itemsize */
988 /* methods */
989 (destructor) mmap_object_dealloc, /* tp_dealloc */
990 0, /* tp_print */
991 0, /* tp_getattr */
992 0, /* tp_setattr */
993 0, /* tp_reserved */
994 0, /* tp_repr */
995 0, /* tp_as_number */
996 &mmap_as_sequence, /*tp_as_sequence*/
997 &mmap_as_mapping, /*tp_as_mapping*/
998 0, /*tp_hash*/
999 0, /*tp_call*/
1000 0, /*tp_str*/
1001 PyObject_GenericGetAttr, /*tp_getattro*/
1002 0, /*tp_setattro*/
1003 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001004 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001005 mmap_doc, /*tp_doc*/
1006 0, /* tp_traverse */
1007 0, /* tp_clear */
1008 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001009 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001010 0, /* tp_iter */
1011 0, /* tp_iternext */
1012 mmap_object_methods, /* tp_methods */
1013 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001014 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001015 0, /* tp_base */
1016 0, /* tp_dict */
1017 0, /* tp_descr_get */
1018 0, /* tp_descr_set */
1019 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001020 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001021 PyType_GenericAlloc, /* tp_alloc */
1022 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001023 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001024};
1025
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001026
Tim Petersec0a5f02006-02-16 23:47:20 +00001027#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001028#ifdef HAVE_LARGEFILE_SUPPORT
1029#define _Py_PARSE_OFF_T "L"
1030#else
1031#define _Py_PARSE_OFF_T "l"
1032#endif
1033
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001034static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001035new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001036{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001037 struct _Py_stat_struct status;
Zackery Spytzd6e14042018-03-14 14:08:01 -06001038 int fstat_result = -1;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001039 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001040 Py_ssize_t map_size;
1041 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001042 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1043 int devzero = -1;
1044 int access = (int)ACCESS_DEFAULT;
1045 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001046 "flags", "prot",
1047 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001048
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001049 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1050 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001051 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001052 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001053 if (map_size < 0) {
1054 PyErr_SetString(PyExc_OverflowError,
Zackery Spytz9308dea2018-03-21 00:02:37 -06001055 "memory mapped length must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001056 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001057 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001058 if (offset < 0) {
1059 PyErr_SetString(PyExc_OverflowError,
1060 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001061 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001062 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001063
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001064 if ((access != (int)ACCESS_DEFAULT) &&
1065 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1066 return PyErr_Format(PyExc_ValueError,
1067 "mmap can't specify both access and flags, prot.");
1068 switch ((access_mode)access) {
1069 case ACCESS_READ:
1070 flags = MAP_SHARED;
1071 prot = PROT_READ;
1072 break;
1073 case ACCESS_WRITE:
1074 flags = MAP_SHARED;
1075 prot = PROT_READ | PROT_WRITE;
1076 break;
1077 case ACCESS_COPY:
1078 flags = MAP_PRIVATE;
1079 prot = PROT_READ | PROT_WRITE;
1080 break;
1081 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001082 /* map prot to access type */
1083 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1084 /* ACCESS_DEFAULT */
1085 }
1086 else if (prot & PROT_WRITE) {
1087 access = ACCESS_WRITE;
1088 }
1089 else {
1090 access = ACCESS_READ;
1091 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001092 break;
1093 default:
1094 return PyErr_Format(PyExc_ValueError,
1095 "mmap invalid access parameter.");
1096 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001097
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001098#ifdef __APPLE__
1099 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1100 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1101 if (fd != -1)
1102 (void)fcntl(fd, F_FULLFSYNC);
1103#endif
Nir Soffer4484f9d2018-03-12 01:39:22 +02001104
1105 if (fd != -1) {
1106 Py_BEGIN_ALLOW_THREADS
1107 fstat_result = _Py_fstat_noraise(fd, &status);
1108 Py_END_ALLOW_THREADS
1109 }
1110
1111 if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001112 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001113 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001114 PyErr_SetString(PyExc_ValueError,
1115 "cannot mmap an empty file");
1116 return NULL;
1117 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001118 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001119 PyErr_SetString(PyExc_ValueError,
1120 "mmap offset is greater than file size");
1121 return NULL;
1122 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001123 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001124 PyErr_SetString(PyExc_ValueError,
1125 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001126 return NULL;
1127 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001128 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001129 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001130 PyErr_SetString(PyExc_ValueError,
1131 "mmap length is greater than file size");
1132 return NULL;
1133 }
1134 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001135 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1136 if (m_obj == NULL) {return NULL;}
1137 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001138 m_obj->size = map_size;
1139 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001140 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001141 m_obj->exports = 0;
1142 m_obj->offset = offset;
1143 if (fd == -1) {
1144 m_obj->fd = -1;
1145 /* Assume the caller wants to map anonymous memory.
1146 This is the same behaviour as Windows. mmap.mmap(-1, size)
1147 on both Windows and Unix map anonymous memory.
1148 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001149#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001150 /* BSD way to map anonymous memory */
1151 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001152#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001153 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001154 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001155 if (devzero == -1) {
1156 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001157 return NULL;
1158 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001159#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001160 }
1161 else {
1162 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001163 if (m_obj->fd == -1) {
1164 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001165 return NULL;
1166 }
1167 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001168
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001169 m_obj->data = mmap(NULL, map_size,
1170 prot, flags,
1171 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001172
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001173 if (devzero != -1) {
1174 close(devzero);
1175 }
1176
1177 if (m_obj->data == (char *)-1) {
1178 m_obj->data = NULL;
1179 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001180 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001181 return NULL;
1182 }
1183 m_obj->access = (access_mode)access;
1184 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001185}
1186#endif /* UNIX */
1187
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001188#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001189
1190/* A note on sizes and offsets: while the actual map size must hold in a
1191 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001192 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001193*/
1194
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001195static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001196new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001197{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001198 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001199 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001200 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001201 DWORD off_hi; /* upper 32 bits of offset */
1202 DWORD off_lo; /* lower 32 bits of offset */
1203 DWORD size_hi; /* upper 32 bits of size */
1204 DWORD size_lo; /* lower 32 bits of size */
Serhiy Storchakae2f92de2017-11-11 13:06:26 +02001205 const char *tagname = "";
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001206 DWORD dwErr = 0;
1207 int fileno;
1208 HANDLE fh = 0;
1209 int access = (access_mode)ACCESS_DEFAULT;
1210 DWORD flProtect, dwDesiredAccess;
1211 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001212 "tagname",
1213 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001214
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001215 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1216 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001217 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001218 return NULL;
1219 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001220
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001221 switch((access_mode)access) {
1222 case ACCESS_READ:
1223 flProtect = PAGE_READONLY;
1224 dwDesiredAccess = FILE_MAP_READ;
1225 break;
1226 case ACCESS_DEFAULT: case ACCESS_WRITE:
1227 flProtect = PAGE_READWRITE;
1228 dwDesiredAccess = FILE_MAP_WRITE;
1229 break;
1230 case ACCESS_COPY:
1231 flProtect = PAGE_WRITECOPY;
1232 dwDesiredAccess = FILE_MAP_COPY;
1233 break;
1234 default:
1235 return PyErr_Format(PyExc_ValueError,
1236 "mmap invalid access parameter.");
1237 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001238
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001239 if (map_size < 0) {
1240 PyErr_SetString(PyExc_OverflowError,
Zackery Spytz9308dea2018-03-21 00:02:37 -06001241 "memory mapped length must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001242 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001243 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001244 if (offset < 0) {
1245 PyErr_SetString(PyExc_OverflowError,
1246 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001247 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001248 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001249
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001250 /* assume -1 and 0 both mean invalid filedescriptor
1251 to 'anonymously' map memory.
1252 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1253 XXX: Should this code be added?
1254 if (fileno == 0)
1255 PyErr_WarnEx(PyExc_DeprecationWarning,
1256 "don't use 0 for anonymous memory",
1257 1);
1258 */
1259 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001260 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001261 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001262 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001263 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001264 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001265 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001266 return NULL;
1267 }
1268 /* Win9x appears to need us seeked to zero */
1269 lseek(fileno, 0, SEEK_SET);
1270 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001271
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001272 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1273 if (m_obj == NULL)
1274 return NULL;
1275 /* Set every field to an invalid marker, so we can safely
1276 destruct the object in the face of failure */
1277 m_obj->data = NULL;
1278 m_obj->file_handle = INVALID_HANDLE_VALUE;
1279 m_obj->map_handle = NULL;
1280 m_obj->tagname = NULL;
1281 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001282
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001283 if (fh) {
1284 /* It is necessary to duplicate the handle, so the
1285 Python code can close it on us */
1286 if (!DuplicateHandle(
1287 GetCurrentProcess(), /* source process handle */
1288 fh, /* handle to be duplicated */
1289 GetCurrentProcess(), /* target proc handle */
1290 (LPHANDLE)&m_obj->file_handle, /* result */
1291 0, /* access - ignored due to options value */
1292 FALSE, /* inherited by child processes? */
1293 DUPLICATE_SAME_ACCESS)) { /* options */
1294 dwErr = GetLastError();
1295 Py_DECREF(m_obj);
1296 PyErr_SetFromWindowsErr(dwErr);
1297 return NULL;
1298 }
1299 if (!map_size) {
1300 DWORD low,high;
1301 low = GetFileSize(fh, &high);
1302 /* low might just happen to have the value INVALID_FILE_SIZE;
1303 so we need to check the last error also. */
1304 if (low == INVALID_FILE_SIZE &&
1305 (dwErr = GetLastError()) != NO_ERROR) {
1306 Py_DECREF(m_obj);
1307 return PyErr_SetFromWindowsErr(dwErr);
1308 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001309
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001310 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001311 if (size == 0) {
1312 PyErr_SetString(PyExc_ValueError,
1313 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001314 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001315 return NULL;
1316 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001317 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001318 PyErr_SetString(PyExc_ValueError,
1319 "mmap offset is greater than file size");
1320 Py_DECREF(m_obj);
1321 return NULL;
1322 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001323 if (size - offset > PY_SSIZE_T_MAX) {
1324 PyErr_SetString(PyExc_ValueError,
1325 "mmap length is too large");
1326 Py_DECREF(m_obj);
1327 return NULL;
1328 }
1329 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001330 } else {
1331 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001332 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001333 }
1334 }
1335 else {
1336 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001337 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001338 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001339
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001340 /* set the initial position */
1341 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001342
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001343 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001344 m_obj->exports = 0;
1345 /* set the tag name */
1346 if (tagname != NULL && *tagname != '\0') {
1347 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1348 if (m_obj->tagname == NULL) {
1349 PyErr_NoMemory();
1350 Py_DECREF(m_obj);
1351 return NULL;
1352 }
1353 strcpy(m_obj->tagname, tagname);
1354 }
1355 else
1356 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001357
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001358 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001359 size_hi = (DWORD)(size >> 32);
1360 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001361 off_hi = (DWORD)(offset >> 32);
1362 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001363 /* For files, it would be sufficient to pass 0 as size.
1364 For anonymous maps, we have to pass the size explicitly. */
1365 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1366 NULL,
1367 flProtect,
1368 size_hi,
1369 size_lo,
1370 m_obj->tagname);
1371 if (m_obj->map_handle != NULL) {
1372 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1373 dwDesiredAccess,
1374 off_hi,
1375 off_lo,
1376 m_obj->size);
1377 if (m_obj->data != NULL)
1378 return (PyObject *)m_obj;
1379 else {
1380 dwErr = GetLastError();
1381 CloseHandle(m_obj->map_handle);
1382 m_obj->map_handle = NULL;
1383 }
1384 } else
1385 dwErr = GetLastError();
1386 Py_DECREF(m_obj);
1387 PyErr_SetFromWindowsErr(dwErr);
1388 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001389}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001390#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001391
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001392static void
1393setint(PyObject *d, const char *name, long value)
1394{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001395 PyObject *o = PyLong_FromLong(value);
1396 if (o && PyDict_SetItemString(d, name, o) == 0) {
1397 Py_DECREF(o);
1398 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001399}
1400
Martin v. Löwis1a214512008-06-11 05:26:20 +00001401
1402static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001403 PyModuleDef_HEAD_INIT,
1404 "mmap",
1405 NULL,
1406 -1,
1407 NULL,
1408 NULL,
1409 NULL,
1410 NULL,
1411 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001412};
1413
Mark Hammond62b1ab12002-07-23 06:31:15 +00001414PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001415PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001416{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001417 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001418
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001419 if (PyType_Ready(&mmap_object_type) < 0)
1420 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001421
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001422 module = PyModule_Create(&mmapmodule);
1423 if (module == NULL)
1424 return NULL;
1425 dict = PyModule_GetDict(module);
1426 if (!dict)
1427 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001428 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001429 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001430#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001431 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001432#endif
1433#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001434 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001435#endif
1436#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001437 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001438#endif
1439
1440#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001441 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001442#endif
1443#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001444 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001445#endif
1446#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001447 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001448#endif
1449#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001450 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001451#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001452#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001453 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1454 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001455#endif
1456
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001457 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001458
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001459 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001460
Justus Schwabedal5a8a84b2017-11-07 15:51:43 -05001461 setint(dict, "ACCESS_DEFAULT", ACCESS_DEFAULT);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001462 setint(dict, "ACCESS_READ", ACCESS_READ);
1463 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1464 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1465 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001466}