blob: 5f1615ff82992c9bea57a2ab522c7f5f33d50459 [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
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000184 Py_INCREF(Py_None);
185 return Py_None;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000186}
187
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000188#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000189#define CHECK_VALID(err) \
190do { \
191 if (self->map_handle == NULL) { \
192 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
193 return err; \
194 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000195} while (0)
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000196#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000197
198#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000199#define CHECK_VALID(err) \
200do { \
201 if (self->data == NULL) { \
202 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
203 return err; \
204 } \
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000205} while (0)
206#endif /* UNIX */
207
208static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000209mmap_read_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000210 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000211{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000212 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700213 if (self->pos >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000214 PyErr_SetString(PyExc_ValueError, "read byte out of range");
215 return NULL;
216 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700217 return PyLong_FromLong((unsigned char)self->data[self->pos++]);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000218}
219
220static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000221mmap_read_line_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000222 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000223{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700224 Py_ssize_t remaining;
225 char *start, *eol;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000226 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000227
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000228 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000229
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700230 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
231 if (!remaining)
232 return PyBytes_FromString("");
233 start = self->data + self->pos;
234 eol = memchr(start, '\n', remaining);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000235 if (!eol)
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700236 eol = self->data + self->size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000237 else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700238 ++eol; /* advance past newline */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000239 result = PyBytes_FromStringAndSize(start, (eol - start));
240 self->pos += (eol - start);
241 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000242}
243
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200244/* Basically the "n" format code with the ability to turn None into -1. */
245static int
246mmap_convert_ssize_t(PyObject *obj, void *result) {
247 Py_ssize_t limit;
248 if (obj == Py_None) {
249 limit = -1;
250 }
251 else if (PyNumber_Check(obj)) {
252 limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
253 if (limit == -1 && PyErr_Occurred())
254 return 0;
255 }
256 else {
257 PyErr_Format(PyExc_TypeError,
258 "integer argument expected, got '%.200s'",
259 Py_TYPE(obj)->tp_name);
260 return 0;
261 }
262 *((Py_ssize_t *)result) = limit;
263 return 1;
264}
265
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000266static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000267mmap_read_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000268 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000269{
Benjamin Peterson8f1cdc62016-10-05 23:32:09 -0700270 Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000271 PyObject *result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000272
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000273 CHECK_VALID(NULL);
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200274 if (!PyArg_ParseTuple(args, "|O&:read", mmap_convert_ssize_t, &num_bytes))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000275 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000276
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000277 /* silently 'adjust' out-of-range requests */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700278 remaining = (self->pos < self->size) ? self->size - self->pos : 0;
279 if (num_bytes < 0 || num_bytes > remaining)
280 num_bytes = remaining;
281 result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000282 self->pos += num_bytes;
283 return result;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000284}
285
286static PyObject *
Georg Brandlfceab5a2008-01-19 20:08:23 +0000287mmap_gfind(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000288 PyObject *args,
289 int reverse)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000290{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000291 Py_ssize_t start = self->pos;
292 Py_ssize_t end = self->size;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200293 Py_buffer view;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000294
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000295 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200296 if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
297 &view, &start, &end)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000298 return NULL;
299 } else {
300 const char *p, *start_p, *end_p;
301 int sign = reverse ? -1 : 1;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200302 const char *needle = view.buf;
303 Py_ssize_t len = view.len;
Greg Stein834f4dd2001-05-14 09:32:26 +0000304
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000305 if (start < 0)
306 start += self->size;
307 if (start < 0)
308 start = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700309 else if (start > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000310 start = self->size;
Greg Stein834f4dd2001-05-14 09:32:26 +0000311
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000312 if (end < 0)
313 end += self->size;
314 if (end < 0)
315 end = 0;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700316 else if (end > self->size)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000317 end = self->size;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000318
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000319 start_p = self->data + start;
320 end_p = self->data + end;
Georg Brandlfceab5a2008-01-19 20:08:23 +0000321
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000322 for (p = (reverse ? end_p - len : start_p);
323 (p >= start_p) && (p + len <= end_p); p += sign) {
324 Py_ssize_t i;
325 for (i = 0; i < len && needle[i] == p[i]; ++i)
326 /* nothing */;
327 if (i == len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200328 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000329 return PyLong_FromSsize_t(p - self->data);
330 }
331 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200332 PyBuffer_Release(&view);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000333 return PyLong_FromLong(-1);
334 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000335}
336
Georg Brandlfceab5a2008-01-19 20:08:23 +0000337static PyObject *
338mmap_find_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000339 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000340{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000341 return mmap_gfind(self, args, 0);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000342}
343
344static PyObject *
345mmap_rfind_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000346 PyObject *args)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000347{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000348 return mmap_gfind(self, args, 1);
Georg Brandlfceab5a2008-01-19 20:08:23 +0000349}
350
Tim Petersec0a5f02006-02-16 23:47:20 +0000351static int
Sean Reifscheider54cf12b2007-09-17 17:55:36 +0000352is_writable(mmap_object *self)
Tim Peters5ebfd362001-11-13 23:11:19 +0000353{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000354 if (self->access != ACCESS_READ)
355 return 1;
356 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
357 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000358}
359
Tim Petersec0a5f02006-02-16 23:47:20 +0000360static int
Tim Peters5ebfd362001-11-13 23:11:19 +0000361is_resizeable(mmap_object *self)
362{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000363 if (self->exports > 0) {
364 PyErr_SetString(PyExc_BufferError,
365 "mmap can't resize with extant buffers exported.");
366 return 0;
367 }
368 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
369 return 1;
370 PyErr_Format(PyExc_TypeError,
371 "mmap can't resize a readonly or copy-on-write memory map.");
372 return 0;
Tim Peters5ebfd362001-11-13 23:11:19 +0000373}
374
375
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000376static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000377mmap_write_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000378 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000379{
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200380 Py_buffer data;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000381
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000382 CHECK_VALID(NULL);
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200383 if (!PyArg_ParseTuple(args, "y*:write", &data))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000384 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000385
Benjamin Peterson37768362016-10-05 23:29:07 -0700386 if (!is_writable(self)) {
387 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700388 return NULL;
Benjamin Peterson37768362016-10-05 23:29:07 -0700389 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700390
391 if (self->pos > self->size || self->size - self->pos < data.len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200392 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700393 PyErr_SetString(PyExc_ValueError, "data out of range");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000394 return NULL;
395 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200396
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700397 memcpy(&self->data[self->pos], data.buf, data.len);
398 self->pos += data.len;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200399 PyBuffer_Release(&data);
Benjamin Peterson87845bc2016-10-05 22:54:19 -0700400 return PyLong_FromSsize_t(data.len);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000401}
402
403static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000404mmap_write_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000406{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000407 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000408
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000409 CHECK_VALID(NULL);
410 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
411 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000412
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000413 if (!is_writable(self))
414 return NULL;
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000415
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000416 if (self->pos < self->size) {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700417 self->data[self->pos++] = value;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000418 Py_INCREF(Py_None);
419 return Py_None;
420 }
421 else {
422 PyErr_SetString(PyExc_ValueError, "write byte out of range");
423 return NULL;
424 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000425}
Tim Petersec0a5f02006-02-16 23:47:20 +0000426
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000427static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000428mmap_size_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000429 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000430{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000431 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000432
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000433#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000434 if (self->file_handle != INVALID_HANDLE_VALUE) {
435 DWORD low,high;
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700436 long long size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000437 low = GetFileSize(self->file_handle, &high);
438 if (low == INVALID_FILE_SIZE) {
439 /* It might be that the function appears to have failed,
440 when indeed its size equals INVALID_FILE_SIZE */
441 DWORD error = GetLastError();
442 if (error != NO_ERROR)
443 return PyErr_SetFromWindowsErr(error);
444 }
445 if (!high && low < LONG_MAX)
446 return PyLong_FromLong((long)low);
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700447 size = (((long long)high)<<32) + low;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000448 return PyLong_FromLongLong(size);
449 } else {
450 return PyLong_FromSsize_t(self->size);
451 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000452#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000453
454#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000455 {
Victor Stinnere134a7f2015-03-30 10:09:31 +0200456 struct _Py_stat_struct status;
457 if (_Py_fstat(self->fd, &status) == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000458 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000459#ifdef HAVE_LARGEFILE_SUPPORT
Victor Stinnere134a7f2015-03-30 10:09:31 +0200460 return PyLong_FromLongLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000461#else
Victor Stinnere134a7f2015-03-30 10:09:31 +0200462 return PyLong_FromLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000463#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000464 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000465#endif /* UNIX */
466}
467
468/* This assumes that you want the entire file mapped,
469 / and when recreating the map will make the new file
470 / have the new size
471 /
472 / Is this really necessary? This could easily be done
473 / from python by just closing and re-opening with the
474 / new size?
475 */
476
477static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000478mmap_resize_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000479 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000480{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000481 Py_ssize_t new_size;
482 CHECK_VALID(NULL);
483 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
484 !is_resizeable(self)) {
485 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700486 }
487 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
488 PyErr_SetString(PyExc_ValueError, "new size out of range");
489 return NULL;
490 }
491
492 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000493#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000494 DWORD dwErrCode = 0;
495 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
496 /* First, unmap the file view */
497 UnmapViewOfFile(self->data);
498 self->data = NULL;
499 /* Close the mapping object */
500 CloseHandle(self->map_handle);
501 self->map_handle = NULL;
502 /* Move to the desired EOF position */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000503 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
504 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
505 off_hi = (DWORD)(self->offset >> 32);
506 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000507 SetFilePointer(self->file_handle,
508 newSizeLow, &newSizeHigh, FILE_BEGIN);
509 /* Change the size of the file */
510 SetEndOfFile(self->file_handle);
511 /* Create another mapping object and remap the file view */
512 self->map_handle = CreateFileMapping(
513 self->file_handle,
514 NULL,
515 PAGE_READWRITE,
516 0,
517 0,
518 self->tagname);
519 if (self->map_handle != NULL) {
520 self->data = (char *) MapViewOfFile(self->map_handle,
521 FILE_MAP_WRITE,
522 off_hi,
523 off_lo,
524 new_size);
525 if (self->data != NULL) {
526 self->size = new_size;
527 Py_INCREF(Py_None);
528 return Py_None;
529 } else {
530 dwErrCode = GetLastError();
531 CloseHandle(self->map_handle);
532 self->map_handle = NULL;
533 }
534 } else {
535 dwErrCode = GetLastError();
536 }
537 PyErr_SetFromWindowsErr(dwErrCode);
538 return NULL;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000539#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000540
541#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000542#ifndef HAVE_MREMAP
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000543 PyErr_SetString(PyExc_SystemError,
544 "mmap: resizing not available--no mremap()");
545 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000546#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000547 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000548
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700549 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200550 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000551 return NULL;
552 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000553
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000554#ifdef MREMAP_MAYMOVE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000555 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000556#else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700557#if defined(__NetBSD__)
558 newmap = mremap(self->data, self->size, self->data, new_size, 0);
559#else
560 newmap = mremap(self->data, self->size, new_size, 0);
561#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000562#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000563 if (newmap == (void *)-1)
564 {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200565 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000566 return NULL;
567 }
568 self->data = newmap;
569 self->size = new_size;
570 Py_INCREF(Py_None);
571 return Py_None;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000572#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000573#endif /* UNIX */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000574 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000575}
576
577static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000578mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000579{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000580 CHECK_VALID(NULL);
581 return PyLong_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000582}
583
584static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000585mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000586{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000587 Py_ssize_t offset = 0;
588 Py_ssize_t size = self->size;
589 CHECK_VALID(NULL);
590 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
591 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700592 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000593 PyErr_SetString(PyExc_ValueError, "flush values out of range");
594 return NULL;
595 }
R. David Murraye194dd62010-10-18 01:14:06 +0000596
597 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
598 return PyLong_FromLong(0);
599
Christian Heimesaf98da12008-01-27 15:18:18 +0000600#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000601 return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
Christian Heimesaf98da12008-01-27 15:18:18 +0000602#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000603 /* XXX semantics of return value? */
604 /* XXX flags for msync? */
605 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200606 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000607 return NULL;
608 }
609 return PyLong_FromLong(0);
Christian Heimesaf98da12008-01-27 15:18:18 +0000610#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000611 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
612 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000613#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000614}
615
616static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000617mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000618{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000619 Py_ssize_t dist;
620 int how=0;
621 CHECK_VALID(NULL);
622 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
623 return NULL;
624 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700625 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000626 switch (how) {
627 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000628 where = dist;
629 break;
630 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700631 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000632 goto onoutofrange;
633 where = self->pos + dist;
634 break;
635 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700636 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000637 goto onoutofrange;
638 where = self->size + dist;
639 break;
640 default:
641 PyErr_SetString(PyExc_ValueError, "unknown seek type");
642 return NULL;
643 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700644 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000645 goto onoutofrange;
646 self->pos = where;
647 Py_INCREF(Py_None);
648 return Py_None;
649 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000650
Tim Peters5ebfd362001-11-13 23:11:19 +0000651 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000652 PyErr_SetString(PyExc_ValueError, "seek out of range");
653 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000654}
655
656static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000657mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000658{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700659 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000660 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700661 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000662 !is_writable(self)) {
663 return NULL;
664 } else {
665 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700666 if (dest < 0 || src < 0 || cnt < 0)
667 goto bounds;
668 if (self->size - dest < cnt || self->size - src < cnt)
669 goto bounds;
670
671 memmove(&self->data[dest], &self->data[src], cnt);
672
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000673 Py_INCREF(Py_None);
674 return Py_None;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700675
676 bounds:
677 PyErr_SetString(PyExc_ValueError,
678 "source, destination, or count out of range");
679 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000680 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000681}
682
Georg Brandl0bccc182010-08-01 14:50:00 +0000683static PyObject *
684mmap_closed_get(mmap_object *self)
685{
686#ifdef MS_WINDOWS
687 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
688#elif defined(UNIX)
689 return PyBool_FromLong(self->data == NULL ? 1 : 0);
690#endif
691}
692
693static PyObject *
694mmap__enter__method(mmap_object *self, PyObject *args)
695{
696 CHECK_VALID(NULL);
697
698 Py_INCREF(self);
699 return (PyObject *)self;
700}
701
702static PyObject *
703mmap__exit__method(PyObject *self, PyObject *args)
704{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200705 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200706
707 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000708}
709
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300710#ifdef MS_WINDOWS
711static PyObject *
712mmap__sizeof__method(mmap_object *self, void *unused)
713{
714 Py_ssize_t res;
715
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200716 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300717 if (self->tagname)
718 res += strlen(self->tagname) + 1;
719 return PyLong_FromSsize_t(res);
720}
721#endif
722
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000723static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000724 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
725 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
726 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
727 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
728 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
729 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
730 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
731 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
732 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
733 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
734 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
735 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
736 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
737 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000738 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
739 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300740#ifdef MS_WINDOWS
741 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
742#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000743 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000744};
745
Georg Brandl0bccc182010-08-01 14:50:00 +0000746static PyGetSetDef mmap_object_getset[] = {
747 {"closed", (getter) mmap_closed_get, NULL, NULL},
748 {NULL}
749};
750
751
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000752/* Functions for treating an mmap'ed file as a buffer */
753
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000754static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000755mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000756{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000757 CHECK_VALID(-1);
758 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
759 (self->access == ACCESS_READ), flags) < 0)
760 return -1;
761 self->exports++;
762 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000763}
764
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000765static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000766mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000767{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000768 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000769}
770
Martin v. Löwis18e16552006-02-15 17:27:45 +0000771static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000772mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000773{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000774 CHECK_VALID(-1);
775 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000776}
777
778static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000779mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000780{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000781 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700782 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000783 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
784 return NULL;
785 }
786 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000787}
788
789static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000790mmap_subscript(mmap_object *self, PyObject *item)
791{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000792 CHECK_VALID(NULL);
793 if (PyIndex_Check(item)) {
794 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
795 if (i == -1 && PyErr_Occurred())
796 return NULL;
797 if (i < 0)
798 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700799 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000800 PyErr_SetString(PyExc_IndexError,
801 "mmap index out of range");
802 return NULL;
803 }
804 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
805 }
806 else if (PySlice_Check(item)) {
807 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000808
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000809 if (PySlice_GetIndicesEx(item, self->size,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000810 &start, &stop, &step, &slicelen) < 0) {
811 return NULL;
812 }
Guido van Rossum98297ee2007-11-06 21:34:58 +0000813
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000814 if (slicelen <= 0)
815 return PyBytes_FromStringAndSize("", 0);
816 else if (step == 1)
817 return PyBytes_FromStringAndSize(self->data + start,
818 slicelen);
819 else {
820 char *result_buf = (char *)PyMem_Malloc(slicelen);
821 Py_ssize_t cur, i;
822 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000823
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000824 if (result_buf == NULL)
825 return PyErr_NoMemory();
826 for (cur = start, i = 0; i < slicelen;
827 cur += step, i++) {
828 result_buf[i] = self->data[cur];
829 }
830 result = PyBytes_FromStringAndSize(result_buf,
831 slicelen);
832 PyMem_Free(result_buf);
833 return result;
834 }
835 }
836 else {
837 PyErr_SetString(PyExc_TypeError,
838 "mmap indices must be integers");
839 return NULL;
840 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000841}
842
843static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000844mmap_concat(mmap_object *self, PyObject *bb)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000845{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000846 CHECK_VALID(NULL);
847 PyErr_SetString(PyExc_SystemError,
848 "mmaps don't support concatenation");
849 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000850}
851
852static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000853mmap_repeat(mmap_object *self, Py_ssize_t n)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000854{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000855 CHECK_VALID(NULL);
856 PyErr_SetString(PyExc_SystemError,
857 "mmaps don't support repeat operation");
858 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000859}
860
861static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000862mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000863{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000864 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000865
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000866 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700867 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000868 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
869 return -1;
870 }
871 if (v == NULL) {
872 PyErr_SetString(PyExc_TypeError,
873 "mmap object doesn't support item deletion");
874 return -1;
875 }
876 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
877 PyErr_SetString(PyExc_IndexError,
878 "mmap assignment must be length-1 bytes()");
879 return -1;
880 }
881 if (!is_writable(self))
882 return -1;
883 buf = PyBytes_AsString(v);
884 self->data[i] = buf[0];
885 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000886}
887
Thomas Woutersed03b412007-08-28 21:37:11 +0000888static int
889mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
890{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000891 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000892
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000893 if (!is_writable(self))
894 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000895
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000896 if (PyIndex_Check(item)) {
897 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
898 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000899
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000900 if (i == -1 && PyErr_Occurred())
901 return -1;
902 if (i < 0)
903 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700904 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000905 PyErr_SetString(PyExc_IndexError,
906 "mmap index out of range");
907 return -1;
908 }
909 if (value == NULL) {
910 PyErr_SetString(PyExc_TypeError,
911 "mmap doesn't support item deletion");
912 return -1;
913 }
914 if (!PyIndex_Check(value)) {
915 PyErr_SetString(PyExc_TypeError,
916 "mmap item value must be an int");
917 return -1;
918 }
919 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
920 if (v == -1 && PyErr_Occurred())
921 return -1;
922 if (v < 0 || v > 255) {
923 PyErr_SetString(PyExc_ValueError,
924 "mmap item value must be "
925 "in range(0, 256)");
926 return -1;
927 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000928 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000929 return 0;
930 }
931 else if (PySlice_Check(item)) {
932 Py_ssize_t start, stop, step, slicelen;
933 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000934
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000935 if (PySlice_GetIndicesEx(item,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000936 self->size, &start, &stop,
937 &step, &slicelen) < 0) {
938 return -1;
939 }
940 if (value == NULL) {
941 PyErr_SetString(PyExc_TypeError,
942 "mmap object doesn't support slice deletion");
943 return -1;
944 }
945 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
946 return -1;
947 if (vbuf.len != slicelen) {
948 PyErr_SetString(PyExc_IndexError,
949 "mmap slice assignment is wrong size");
950 PyBuffer_Release(&vbuf);
951 return -1;
952 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000953
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000954 if (slicelen == 0) {
955 }
956 else if (step == 1) {
957 memcpy(self->data + start, vbuf.buf, slicelen);
958 }
959 else {
960 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000961
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000962 for (cur = start, i = 0;
963 i < slicelen;
964 cur += step, i++)
965 {
966 self->data[cur] = ((char *)vbuf.buf)[i];
967 }
968 }
969 PyBuffer_Release(&vbuf);
970 return 0;
971 }
972 else {
973 PyErr_SetString(PyExc_TypeError,
974 "mmap indices must be integer");
975 return -1;
976 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000977}
978
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000979static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100980 (lenfunc)mmap_length, /*sq_length*/
981 (binaryfunc)mmap_concat, /*sq_concat*/
982 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
983 (ssizeargfunc)mmap_item, /*sq_item*/
984 0, /*sq_slice*/
985 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
986 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000987};
988
Thomas Woutersed03b412007-08-28 21:37:11 +0000989static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000990 (lenfunc)mmap_length,
991 (binaryfunc)mmap_subscript,
992 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000993};
994
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000995static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000996 (getbufferproc)mmap_buffer_getbuf,
997 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000998};
999
Georg Brandl86def6c2008-01-21 20:36:10 +00001000static PyObject *
1001new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1002
Christian Heimese1c98112008-01-21 11:20:28 +00001003PyDoc_STRVAR(mmap_doc,
1004"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1005\n\
1006Maps length bytes from the file specified by the file handle fileno,\n\
1007and returns a mmap object. If length is larger than the current size\n\
1008of the file, the file is extended to contain length bytes. If length\n\
1009is 0, the maximum length of the map is the current size of the file,\n\
1010except that if the file is empty Windows raises an exception (you cannot\n\
1011create an empty mapping on Windows).\n\
1012\n\
1013Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1014\n\
1015Maps length bytes from the file specified by the file descriptor fileno,\n\
1016and returns a mmap object. If length is 0, the maximum length of the map\n\
1017will be the current size of the file when mmap is called.\n\
1018flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1019private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +00001020object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +00001021that's shared with all other processes mapping the same areas of the file.\n\
1022The default value is MAP_SHARED.\n\
1023\n\
1024To map anonymous memory, pass -1 as the fileno (both versions).");
1025
1026
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001027static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001028 PyVarObject_HEAD_INIT(NULL, 0)
1029 "mmap.mmap", /* tp_name */
1030 sizeof(mmap_object), /* tp_size */
1031 0, /* tp_itemsize */
1032 /* methods */
1033 (destructor) mmap_object_dealloc, /* tp_dealloc */
1034 0, /* tp_print */
1035 0, /* tp_getattr */
1036 0, /* tp_setattr */
1037 0, /* tp_reserved */
1038 0, /* tp_repr */
1039 0, /* tp_as_number */
1040 &mmap_as_sequence, /*tp_as_sequence*/
1041 &mmap_as_mapping, /*tp_as_mapping*/
1042 0, /*tp_hash*/
1043 0, /*tp_call*/
1044 0, /*tp_str*/
1045 PyObject_GenericGetAttr, /*tp_getattro*/
1046 0, /*tp_setattro*/
1047 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001048 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001049 mmap_doc, /*tp_doc*/
1050 0, /* tp_traverse */
1051 0, /* tp_clear */
1052 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001053 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001054 0, /* tp_iter */
1055 0, /* tp_iternext */
1056 mmap_object_methods, /* tp_methods */
1057 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001058 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001059 0, /* tp_base */
1060 0, /* tp_dict */
1061 0, /* tp_descr_get */
1062 0, /* tp_descr_set */
1063 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001064 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001065 PyType_GenericAlloc, /* tp_alloc */
1066 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001067 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001068};
1069
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001070
Tim Petersec0a5f02006-02-16 23:47:20 +00001071#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001072#ifdef HAVE_LARGEFILE_SUPPORT
1073#define _Py_PARSE_OFF_T "L"
1074#else
1075#define _Py_PARSE_OFF_T "l"
1076#endif
1077
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001078static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001079new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001080{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001081 struct _Py_stat_struct status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001082 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001083 Py_ssize_t map_size;
1084 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001085 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1086 int devzero = -1;
1087 int access = (int)ACCESS_DEFAULT;
1088 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001089 "flags", "prot",
1090 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001091
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001092 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1093 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001094 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001095 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001096 if (map_size < 0) {
1097 PyErr_SetString(PyExc_OverflowError,
1098 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001099 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001100 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001101 if (offset < 0) {
1102 PyErr_SetString(PyExc_OverflowError,
1103 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001104 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001105 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001106
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001107 if ((access != (int)ACCESS_DEFAULT) &&
1108 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1109 return PyErr_Format(PyExc_ValueError,
1110 "mmap can't specify both access and flags, prot.");
1111 switch ((access_mode)access) {
1112 case ACCESS_READ:
1113 flags = MAP_SHARED;
1114 prot = PROT_READ;
1115 break;
1116 case ACCESS_WRITE:
1117 flags = MAP_SHARED;
1118 prot = PROT_READ | PROT_WRITE;
1119 break;
1120 case ACCESS_COPY:
1121 flags = MAP_PRIVATE;
1122 prot = PROT_READ | PROT_WRITE;
1123 break;
1124 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001125 /* map prot to access type */
1126 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1127 /* ACCESS_DEFAULT */
1128 }
1129 else if (prot & PROT_WRITE) {
1130 access = ACCESS_WRITE;
1131 }
1132 else {
1133 access = ACCESS_READ;
1134 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001135 break;
1136 default:
1137 return PyErr_Format(PyExc_ValueError,
1138 "mmap invalid access parameter.");
1139 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001140
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001141#ifdef __APPLE__
1142 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1143 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1144 if (fd != -1)
1145 (void)fcntl(fd, F_FULLFSYNC);
1146#endif
Victor Stinnere134a7f2015-03-30 10:09:31 +02001147 if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1148 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001149 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001150 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001151 PyErr_SetString(PyExc_ValueError,
1152 "cannot mmap an empty file");
1153 return NULL;
1154 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001155 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001156 PyErr_SetString(PyExc_ValueError,
1157 "mmap offset is greater than file size");
1158 return NULL;
1159 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001160 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001161 PyErr_SetString(PyExc_ValueError,
1162 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001163 return NULL;
1164 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001165 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001166 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001167 PyErr_SetString(PyExc_ValueError,
1168 "mmap length is greater than file size");
1169 return NULL;
1170 }
1171 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001172 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1173 if (m_obj == NULL) {return NULL;}
1174 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001175 m_obj->size = map_size;
1176 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001177 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001178 m_obj->exports = 0;
1179 m_obj->offset = offset;
1180 if (fd == -1) {
1181 m_obj->fd = -1;
1182 /* Assume the caller wants to map anonymous memory.
1183 This is the same behaviour as Windows. mmap.mmap(-1, size)
1184 on both Windows and Unix map anonymous memory.
1185 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001186#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001187 /* BSD way to map anonymous memory */
1188 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001189#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001190 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001191 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001192 if (devzero == -1) {
1193 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001194 return NULL;
1195 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001196#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001197 }
1198 else {
1199 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001200 if (m_obj->fd == -1) {
1201 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001202 return NULL;
1203 }
1204 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001205
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001206 m_obj->data = mmap(NULL, map_size,
1207 prot, flags,
1208 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001209
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001210 if (devzero != -1) {
1211 close(devzero);
1212 }
1213
1214 if (m_obj->data == (char *)-1) {
1215 m_obj->data = NULL;
1216 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001217 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001218 return NULL;
1219 }
1220 m_obj->access = (access_mode)access;
1221 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001222}
1223#endif /* UNIX */
1224
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001225#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001226
1227/* A note on sizes and offsets: while the actual map size must hold in a
1228 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001229 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001230*/
1231
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001232static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001233new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001234{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001235 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001236 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001237 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001238 DWORD off_hi; /* upper 32 bits of offset */
1239 DWORD off_lo; /* lower 32 bits of offset */
1240 DWORD size_hi; /* upper 32 bits of size */
1241 DWORD size_lo; /* lower 32 bits of size */
1242 char *tagname = "";
1243 DWORD dwErr = 0;
1244 int fileno;
1245 HANDLE fh = 0;
1246 int access = (access_mode)ACCESS_DEFAULT;
1247 DWORD flProtect, dwDesiredAccess;
1248 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001249 "tagname",
1250 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001251
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001252 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1253 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001254 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001255 return NULL;
1256 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001257
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001258 switch((access_mode)access) {
1259 case ACCESS_READ:
1260 flProtect = PAGE_READONLY;
1261 dwDesiredAccess = FILE_MAP_READ;
1262 break;
1263 case ACCESS_DEFAULT: case ACCESS_WRITE:
1264 flProtect = PAGE_READWRITE;
1265 dwDesiredAccess = FILE_MAP_WRITE;
1266 break;
1267 case ACCESS_COPY:
1268 flProtect = PAGE_WRITECOPY;
1269 dwDesiredAccess = FILE_MAP_COPY;
1270 break;
1271 default:
1272 return PyErr_Format(PyExc_ValueError,
1273 "mmap invalid access parameter.");
1274 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001275
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001276 if (map_size < 0) {
1277 PyErr_SetString(PyExc_OverflowError,
1278 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001279 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001280 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001281 if (offset < 0) {
1282 PyErr_SetString(PyExc_OverflowError,
1283 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001284 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001285 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001286
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001287 /* assume -1 and 0 both mean invalid filedescriptor
1288 to 'anonymously' map memory.
1289 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1290 XXX: Should this code be added?
1291 if (fileno == 0)
1292 PyErr_WarnEx(PyExc_DeprecationWarning,
1293 "don't use 0 for anonymous memory",
1294 1);
1295 */
1296 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001297 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001298 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001299 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001300 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001301 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001302 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001303 return NULL;
1304 }
1305 /* Win9x appears to need us seeked to zero */
1306 lseek(fileno, 0, SEEK_SET);
1307 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001308
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001309 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1310 if (m_obj == NULL)
1311 return NULL;
1312 /* Set every field to an invalid marker, so we can safely
1313 destruct the object in the face of failure */
1314 m_obj->data = NULL;
1315 m_obj->file_handle = INVALID_HANDLE_VALUE;
1316 m_obj->map_handle = NULL;
1317 m_obj->tagname = NULL;
1318 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001319
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001320 if (fh) {
1321 /* It is necessary to duplicate the handle, so the
1322 Python code can close it on us */
1323 if (!DuplicateHandle(
1324 GetCurrentProcess(), /* source process handle */
1325 fh, /* handle to be duplicated */
1326 GetCurrentProcess(), /* target proc handle */
1327 (LPHANDLE)&m_obj->file_handle, /* result */
1328 0, /* access - ignored due to options value */
1329 FALSE, /* inherited by child processes? */
1330 DUPLICATE_SAME_ACCESS)) { /* options */
1331 dwErr = GetLastError();
1332 Py_DECREF(m_obj);
1333 PyErr_SetFromWindowsErr(dwErr);
1334 return NULL;
1335 }
1336 if (!map_size) {
1337 DWORD low,high;
1338 low = GetFileSize(fh, &high);
1339 /* low might just happen to have the value INVALID_FILE_SIZE;
1340 so we need to check the last error also. */
1341 if (low == INVALID_FILE_SIZE &&
1342 (dwErr = GetLastError()) != NO_ERROR) {
1343 Py_DECREF(m_obj);
1344 return PyErr_SetFromWindowsErr(dwErr);
1345 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001346
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001347 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001348 if (size == 0) {
1349 PyErr_SetString(PyExc_ValueError,
1350 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001351 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001352 return NULL;
1353 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001354 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001355 PyErr_SetString(PyExc_ValueError,
1356 "mmap offset is greater than file size");
1357 Py_DECREF(m_obj);
1358 return NULL;
1359 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001360 if (size - offset > PY_SSIZE_T_MAX) {
1361 PyErr_SetString(PyExc_ValueError,
1362 "mmap length is too large");
1363 Py_DECREF(m_obj);
1364 return NULL;
1365 }
1366 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001367 } else {
1368 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001369 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001370 }
1371 }
1372 else {
1373 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001374 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001375 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001376
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001377 /* set the initial position */
1378 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001379
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001380 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001381 m_obj->exports = 0;
1382 /* set the tag name */
1383 if (tagname != NULL && *tagname != '\0') {
1384 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1385 if (m_obj->tagname == NULL) {
1386 PyErr_NoMemory();
1387 Py_DECREF(m_obj);
1388 return NULL;
1389 }
1390 strcpy(m_obj->tagname, tagname);
1391 }
1392 else
1393 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001394
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001395 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001396 size_hi = (DWORD)(size >> 32);
1397 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001398 off_hi = (DWORD)(offset >> 32);
1399 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001400 /* For files, it would be sufficient to pass 0 as size.
1401 For anonymous maps, we have to pass the size explicitly. */
1402 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1403 NULL,
1404 flProtect,
1405 size_hi,
1406 size_lo,
1407 m_obj->tagname);
1408 if (m_obj->map_handle != NULL) {
1409 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1410 dwDesiredAccess,
1411 off_hi,
1412 off_lo,
1413 m_obj->size);
1414 if (m_obj->data != NULL)
1415 return (PyObject *)m_obj;
1416 else {
1417 dwErr = GetLastError();
1418 CloseHandle(m_obj->map_handle);
1419 m_obj->map_handle = NULL;
1420 }
1421 } else
1422 dwErr = GetLastError();
1423 Py_DECREF(m_obj);
1424 PyErr_SetFromWindowsErr(dwErr);
1425 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001426}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001427#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001428
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001429static void
1430setint(PyObject *d, const char *name, long value)
1431{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001432 PyObject *o = PyLong_FromLong(value);
1433 if (o && PyDict_SetItemString(d, name, o) == 0) {
1434 Py_DECREF(o);
1435 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001436}
1437
Martin v. Löwis1a214512008-06-11 05:26:20 +00001438
1439static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001440 PyModuleDef_HEAD_INIT,
1441 "mmap",
1442 NULL,
1443 -1,
1444 NULL,
1445 NULL,
1446 NULL,
1447 NULL,
1448 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001449};
1450
Mark Hammond62b1ab12002-07-23 06:31:15 +00001451PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001452PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001453{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001454 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001455
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001456 if (PyType_Ready(&mmap_object_type) < 0)
1457 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001458
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001459 module = PyModule_Create(&mmapmodule);
1460 if (module == NULL)
1461 return NULL;
1462 dict = PyModule_GetDict(module);
1463 if (!dict)
1464 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001465 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001466 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001467#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001468 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001469#endif
1470#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001471 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001472#endif
1473#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001474 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001475#endif
1476
1477#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001478 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001479#endif
1480#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001481 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001482#endif
1483#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001484 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001485#endif
1486#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001487 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001488#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001489#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001490 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1491 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001492#endif
1493
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001494 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001495
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001496 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001497
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001498 setint(dict, "ACCESS_READ", ACCESS_READ);
1499 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1500 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1501 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001502}