blob: 30084447c88f4da1cd50b69ee76a5c78b4d0f3b4 [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 Petersoncd04db02016-10-05 21:45:48 -0700270 Py_ssize_t num_bytes, 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 Petersoncd04db02016-10-05 21:45:48 -0700386 if (!is_writable(self))
387 return NULL;
388
389 if (self->pos > self->size || self->size - self->pos < data.len) {
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200390 PyBuffer_Release(&data);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700391 PyErr_SetString(PyExc_ValueError, "data out of range");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000392 return NULL;
393 }
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200394
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700395 memcpy(&self->data[self->pos], data.buf, data.len);
396 self->pos += data.len;
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200397 PyBuffer_Release(&data);
Benjamin Peterson87845bc2016-10-05 22:54:19 -0700398 return PyLong_FromSsize_t(data.len);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000399}
400
401static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000402mmap_write_byte_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000403 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000404{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 char value;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000406
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000407 CHECK_VALID(NULL);
408 if (!PyArg_ParseTuple(args, "b:write_byte", &value))
409 return(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000410
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000411 if (!is_writable(self))
412 return NULL;
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000413
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000414 if (self->pos < self->size) {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700415 self->data[self->pos++] = value;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000416 Py_INCREF(Py_None);
417 return Py_None;
418 }
419 else {
420 PyErr_SetString(PyExc_ValueError, "write byte out of range");
421 return NULL;
422 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000423}
Tim Petersec0a5f02006-02-16 23:47:20 +0000424
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000425static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000426mmap_size_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000427 PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000428{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000429 CHECK_VALID(NULL);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000430
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000431#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000432 if (self->file_handle != INVALID_HANDLE_VALUE) {
433 DWORD low,high;
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700434 long long size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000435 low = GetFileSize(self->file_handle, &high);
436 if (low == INVALID_FILE_SIZE) {
437 /* It might be that the function appears to have failed,
438 when indeed its size equals INVALID_FILE_SIZE */
439 DWORD error = GetLastError();
440 if (error != NO_ERROR)
441 return PyErr_SetFromWindowsErr(error);
442 }
443 if (!high && low < LONG_MAX)
444 return PyLong_FromLong((long)low);
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700445 size = (((long long)high)<<32) + low;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000446 return PyLong_FromLongLong(size);
447 } else {
448 return PyLong_FromSsize_t(self->size);
449 }
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000450#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000451
452#ifdef UNIX
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000453 {
Victor Stinnere134a7f2015-03-30 10:09:31 +0200454 struct _Py_stat_struct status;
455 if (_Py_fstat(self->fd, &status) == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000456 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000457#ifdef HAVE_LARGEFILE_SUPPORT
Victor Stinnere134a7f2015-03-30 10:09:31 +0200458 return PyLong_FromLongLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000459#else
Victor Stinnere134a7f2015-03-30 10:09:31 +0200460 return PyLong_FromLong(status.st_size);
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000461#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000462 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000463#endif /* UNIX */
464}
465
466/* This assumes that you want the entire file mapped,
467 / and when recreating the map will make the new file
468 / have the new size
469 /
470 / Is this really necessary? This could easily be done
471 / from python by just closing and re-opening with the
472 / new size?
473 */
474
475static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000476mmap_resize_method(mmap_object *self,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000477 PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000478{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000479 Py_ssize_t new_size;
480 CHECK_VALID(NULL);
481 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
482 !is_resizeable(self)) {
483 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700484 }
485 if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
486 PyErr_SetString(PyExc_ValueError, "new size out of range");
487 return NULL;
488 }
489
490 {
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000491#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000492 DWORD dwErrCode = 0;
493 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
494 /* First, unmap the file view */
495 UnmapViewOfFile(self->data);
496 self->data = NULL;
497 /* Close the mapping object */
498 CloseHandle(self->map_handle);
499 self->map_handle = NULL;
500 /* Move to the desired EOF position */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000501 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
502 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
503 off_hi = (DWORD)(self->offset >> 32);
504 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000505 SetFilePointer(self->file_handle,
506 newSizeLow, &newSizeHigh, FILE_BEGIN);
507 /* Change the size of the file */
508 SetEndOfFile(self->file_handle);
509 /* Create another mapping object and remap the file view */
510 self->map_handle = CreateFileMapping(
511 self->file_handle,
512 NULL,
513 PAGE_READWRITE,
514 0,
515 0,
516 self->tagname);
517 if (self->map_handle != NULL) {
518 self->data = (char *) MapViewOfFile(self->map_handle,
519 FILE_MAP_WRITE,
520 off_hi,
521 off_lo,
522 new_size);
523 if (self->data != NULL) {
524 self->size = new_size;
525 Py_INCREF(Py_None);
526 return Py_None;
527 } else {
528 dwErrCode = GetLastError();
529 CloseHandle(self->map_handle);
530 self->map_handle = NULL;
531 }
532 } else {
533 dwErrCode = GetLastError();
534 }
535 PyErr_SetFromWindowsErr(dwErrCode);
536 return NULL;
Martin v. Löwis6238d2b2002-06-30 15:26:10 +0000537#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000538
539#ifdef UNIX
Tim Petersec0a5f02006-02-16 23:47:20 +0000540#ifndef HAVE_MREMAP
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000541 PyErr_SetString(PyExc_SystemError,
542 "mmap: resizing not available--no mremap()");
543 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000544#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000545 void *newmap;
Armin Rigo335ffe82005-09-20 19:04:02 +0000546
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700547 if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200548 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000549 return NULL;
550 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000551
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000552#ifdef MREMAP_MAYMOVE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000553 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000554#else
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700555#if defined(__NetBSD__)
556 newmap = mremap(self->data, self->size, self->data, new_size, 0);
557#else
558 newmap = mremap(self->data, self->size, new_size, 0);
559#endif /* __NetBSD__ */
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000560#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000561 if (newmap == (void *)-1)
562 {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200563 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000564 return NULL;
565 }
566 self->data = newmap;
567 self->size = new_size;
568 Py_INCREF(Py_None);
569 return Py_None;
Andrew M. Kuchling6fef30e2000-06-18 14:51:21 +0000570#endif /* HAVE_MREMAP */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000571#endif /* UNIX */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000572 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000573}
574
575static PyObject *
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000576mmap_tell_method(mmap_object *self, PyObject *unused)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000577{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000578 CHECK_VALID(NULL);
579 return PyLong_FromSize_t(self->pos);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000580}
581
582static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000583mmap_flush_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000584{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000585 Py_ssize_t offset = 0;
586 Py_ssize_t size = self->size;
587 CHECK_VALID(NULL);
588 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
589 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700590 if (size < 0 || offset < 0 || self->size - offset < size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000591 PyErr_SetString(PyExc_ValueError, "flush values out of range");
592 return NULL;
593 }
R. David Murraye194dd62010-10-18 01:14:06 +0000594
595 if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
596 return PyLong_FromLong(0);
597
Christian Heimesaf98da12008-01-27 15:18:18 +0000598#ifdef MS_WINDOWS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000599 return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
Christian Heimesaf98da12008-01-27 15:18:18 +0000600#elif defined(UNIX)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000601 /* XXX semantics of return value? */
602 /* XXX flags for msync? */
603 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200604 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000605 return NULL;
606 }
607 return PyLong_FromLong(0);
Christian Heimesaf98da12008-01-27 15:18:18 +0000608#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000609 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
610 return NULL;
Christian Heimesaf98da12008-01-27 15:18:18 +0000611#endif
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000612}
613
614static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000615mmap_seek_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000616{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000617 Py_ssize_t dist;
618 int how=0;
619 CHECK_VALID(NULL);
620 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
621 return NULL;
622 else {
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700623 Py_ssize_t where;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000624 switch (how) {
625 case 0: /* relative to start */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000626 where = dist;
627 break;
628 case 1: /* relative to current position */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700629 if (PY_SSIZE_T_MAX - self->pos < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000630 goto onoutofrange;
631 where = self->pos + dist;
632 break;
633 case 2: /* relative to end */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700634 if (PY_SSIZE_T_MAX - self->size < dist)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000635 goto onoutofrange;
636 where = self->size + dist;
637 break;
638 default:
639 PyErr_SetString(PyExc_ValueError, "unknown seek type");
640 return NULL;
641 }
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700642 if (where > self->size || where < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000643 goto onoutofrange;
644 self->pos = where;
645 Py_INCREF(Py_None);
646 return Py_None;
647 }
Andrew M. Kuchling70d27422000-06-18 04:45:14 +0000648
Tim Peters5ebfd362001-11-13 23:11:19 +0000649 onoutofrange:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000650 PyErr_SetString(PyExc_ValueError, "seek out of range");
651 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000652}
653
654static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000655mmap_move_method(mmap_object *self, PyObject *args)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000656{
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700657 Py_ssize_t dest, src, cnt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000658 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700659 if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000660 !is_writable(self)) {
661 return NULL;
662 } else {
663 /* bounds check the values */
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700664 if (dest < 0 || src < 0 || cnt < 0)
665 goto bounds;
666 if (self->size - dest < cnt || self->size - src < cnt)
667 goto bounds;
668
669 memmove(&self->data[dest], &self->data[src], cnt);
670
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000671 Py_INCREF(Py_None);
672 return Py_None;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700673
674 bounds:
675 PyErr_SetString(PyExc_ValueError,
676 "source, destination, or count out of range");
677 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000678 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000679}
680
Georg Brandl0bccc182010-08-01 14:50:00 +0000681static PyObject *
682mmap_closed_get(mmap_object *self)
683{
684#ifdef MS_WINDOWS
685 return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
686#elif defined(UNIX)
687 return PyBool_FromLong(self->data == NULL ? 1 : 0);
688#endif
689}
690
691static PyObject *
692mmap__enter__method(mmap_object *self, PyObject *args)
693{
694 CHECK_VALID(NULL);
695
696 Py_INCREF(self);
697 return (PyObject *)self;
698}
699
700static PyObject *
701mmap__exit__method(PyObject *self, PyObject *args)
702{
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200703 _Py_IDENTIFIER(close);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200704
705 return _PyObject_CallMethodId(self, &PyId_close, NULL);
Georg Brandl0bccc182010-08-01 14:50:00 +0000706}
707
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300708#ifdef MS_WINDOWS
709static PyObject *
710mmap__sizeof__method(mmap_object *self, void *unused)
711{
712 Py_ssize_t res;
713
Serhiy Storchaka5c4064e2015-12-19 20:05:25 +0200714 res = _PyObject_SIZE(Py_TYPE(self));
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300715 if (self->tagname)
716 res += strlen(self->tagname) + 1;
717 return PyLong_FromSsize_t(res);
718}
719#endif
720
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000721static struct PyMethodDef mmap_object_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000722 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
723 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
724 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
725 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
726 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
727 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
728 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
729 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
730 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
731 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
732 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
733 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
734 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
735 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
Georg Brandl0bccc182010-08-01 14:50:00 +0000736 {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
737 {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300738#ifdef MS_WINDOWS
739 {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
740#endif
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000741 {NULL, NULL} /* sentinel */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000742};
743
Georg Brandl0bccc182010-08-01 14:50:00 +0000744static PyGetSetDef mmap_object_getset[] = {
745 {"closed", (getter) mmap_closed_get, NULL, NULL},
746 {NULL}
747};
748
749
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000750/* Functions for treating an mmap'ed file as a buffer */
751
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000752static int
Guido van Rossum98297ee2007-11-06 21:34:58 +0000753mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000754{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000755 CHECK_VALID(-1);
756 if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
757 (self->access == ACCESS_READ), flags) < 0)
758 return -1;
759 self->exports++;
760 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000761}
762
Travis E. Oliphantb99f7622007-08-18 11:21:56 +0000763static void
Travis E. Oliphant8ae62b62007-09-23 02:00:13 +0000764mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
Tim Petersec0a5f02006-02-16 23:47:20 +0000765{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000766 self->exports--;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000767}
768
Martin v. Löwis18e16552006-02-15 17:27:45 +0000769static Py_ssize_t
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000770mmap_length(mmap_object *self)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000771{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000772 CHECK_VALID(-1);
773 return self->size;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000774}
775
776static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000777mmap_item(mmap_object *self, Py_ssize_t i)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000778{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000779 CHECK_VALID(NULL);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700780 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000781 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
782 return NULL;
783 }
784 return PyBytes_FromStringAndSize(self->data + i, 1);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000785}
786
787static PyObject *
Thomas Woutersed03b412007-08-28 21:37:11 +0000788mmap_subscript(mmap_object *self, PyObject *item)
789{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000790 CHECK_VALID(NULL);
791 if (PyIndex_Check(item)) {
792 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
793 if (i == -1 && PyErr_Occurred())
794 return NULL;
795 if (i < 0)
796 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700797 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000798 PyErr_SetString(PyExc_IndexError,
799 "mmap index out of range");
800 return NULL;
801 }
802 return PyLong_FromLong(Py_CHARMASK(self->data[i]));
803 }
804 else if (PySlice_Check(item)) {
805 Py_ssize_t start, stop, step, slicelen;
Thomas Woutersed03b412007-08-28 21:37:11 +0000806
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000807 if (PySlice_GetIndicesEx(item, self->size,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000808 &start, &stop, &step, &slicelen) < 0) {
809 return NULL;
810 }
Guido van Rossum98297ee2007-11-06 21:34:58 +0000811
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000812 if (slicelen <= 0)
813 return PyBytes_FromStringAndSize("", 0);
814 else if (step == 1)
815 return PyBytes_FromStringAndSize(self->data + start,
816 slicelen);
817 else {
818 char *result_buf = (char *)PyMem_Malloc(slicelen);
819 Py_ssize_t cur, i;
820 PyObject *result;
Thomas Woutersed03b412007-08-28 21:37:11 +0000821
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000822 if (result_buf == NULL)
823 return PyErr_NoMemory();
824 for (cur = start, i = 0; i < slicelen;
825 cur += step, i++) {
826 result_buf[i] = self->data[cur];
827 }
828 result = PyBytes_FromStringAndSize(result_buf,
829 slicelen);
830 PyMem_Free(result_buf);
831 return result;
832 }
833 }
834 else {
835 PyErr_SetString(PyExc_TypeError,
836 "mmap indices must be integers");
837 return NULL;
838 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000839}
840
841static PyObject *
Fredrik Lundh54cf3dc2000-07-08 22:05:01 +0000842mmap_concat(mmap_object *self, PyObject *bb)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000843{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000844 CHECK_VALID(NULL);
845 PyErr_SetString(PyExc_SystemError,
846 "mmaps don't support concatenation");
847 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000848}
849
850static PyObject *
Martin v. Löwis18e16552006-02-15 17:27:45 +0000851mmap_repeat(mmap_object *self, Py_ssize_t n)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000852{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000853 CHECK_VALID(NULL);
854 PyErr_SetString(PyExc_SystemError,
855 "mmaps don't support repeat operation");
856 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000857}
858
859static int
Martin v. Löwis18e16552006-02-15 17:27:45 +0000860mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000861{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000862 const char *buf;
Tim Petersec0a5f02006-02-16 23:47:20 +0000863
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000864 CHECK_VALID(-1);
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700865 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000866 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
867 return -1;
868 }
869 if (v == NULL) {
870 PyErr_SetString(PyExc_TypeError,
871 "mmap object doesn't support item deletion");
872 return -1;
873 }
874 if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
875 PyErr_SetString(PyExc_IndexError,
876 "mmap assignment must be length-1 bytes()");
877 return -1;
878 }
879 if (!is_writable(self))
880 return -1;
881 buf = PyBytes_AsString(v);
882 self->data[i] = buf[0];
883 return 0;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000884}
885
Thomas Woutersed03b412007-08-28 21:37:11 +0000886static int
887mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
888{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000889 CHECK_VALID(-1);
Thomas Woutersed03b412007-08-28 21:37:11 +0000890
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000891 if (!is_writable(self))
892 return -1;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000893
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000894 if (PyIndex_Check(item)) {
895 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
896 Py_ssize_t v;
Thomas Woutersed03b412007-08-28 21:37:11 +0000897
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000898 if (i == -1 && PyErr_Occurred())
899 return -1;
900 if (i < 0)
901 i += self->size;
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700902 if (i < 0 || i >= self->size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000903 PyErr_SetString(PyExc_IndexError,
904 "mmap index out of range");
905 return -1;
906 }
907 if (value == NULL) {
908 PyErr_SetString(PyExc_TypeError,
909 "mmap doesn't support item deletion");
910 return -1;
911 }
912 if (!PyIndex_Check(value)) {
913 PyErr_SetString(PyExc_TypeError,
914 "mmap item value must be an int");
915 return -1;
916 }
917 v = PyNumber_AsSsize_t(value, PyExc_TypeError);
918 if (v == -1 && PyErr_Occurred())
919 return -1;
920 if (v < 0 || v > 255) {
921 PyErr_SetString(PyExc_ValueError,
922 "mmap item value must be "
923 "in range(0, 256)");
924 return -1;
925 }
Antoine Pitrou22e41552010-08-15 18:07:50 +0000926 self->data[i] = (char) v;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000927 return 0;
928 }
929 else if (PySlice_Check(item)) {
930 Py_ssize_t start, stop, step, slicelen;
931 Py_buffer vbuf;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000932
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000933 if (PySlice_GetIndicesEx(item,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000934 self->size, &start, &stop,
935 &step, &slicelen) < 0) {
936 return -1;
937 }
938 if (value == NULL) {
939 PyErr_SetString(PyExc_TypeError,
940 "mmap object doesn't support slice deletion");
941 return -1;
942 }
943 if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
944 return -1;
945 if (vbuf.len != slicelen) {
946 PyErr_SetString(PyExc_IndexError,
947 "mmap slice assignment is wrong size");
948 PyBuffer_Release(&vbuf);
949 return -1;
950 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000951
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000952 if (slicelen == 0) {
953 }
954 else if (step == 1) {
955 memcpy(self->data + start, vbuf.buf, slicelen);
956 }
957 else {
958 Py_ssize_t cur, i;
Guido van Rossum98297ee2007-11-06 21:34:58 +0000959
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000960 for (cur = start, i = 0;
961 i < slicelen;
962 cur += step, i++)
963 {
964 self->data[cur] = ((char *)vbuf.buf)[i];
965 }
966 }
967 PyBuffer_Release(&vbuf);
968 return 0;
969 }
970 else {
971 PyErr_SetString(PyExc_TypeError,
972 "mmap indices must be integer");
973 return -1;
974 }
Thomas Woutersed03b412007-08-28 21:37:11 +0000975}
976
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000977static PySequenceMethods mmap_as_sequence = {
Stefan Krah23186992012-03-06 15:37:36 +0100978 (lenfunc)mmap_length, /*sq_length*/
979 (binaryfunc)mmap_concat, /*sq_concat*/
980 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
981 (ssizeargfunc)mmap_item, /*sq_item*/
982 0, /*sq_slice*/
983 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
984 0, /*sq_ass_slice*/
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000985};
986
Thomas Woutersed03b412007-08-28 21:37:11 +0000987static PyMappingMethods mmap_as_mapping = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000988 (lenfunc)mmap_length,
989 (binaryfunc)mmap_subscript,
990 (objobjargproc)mmap_ass_subscript,
Thomas Woutersed03b412007-08-28 21:37:11 +0000991};
992
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000993static PyBufferProcs mmap_as_buffer = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000994 (getbufferproc)mmap_buffer_getbuf,
995 (releasebufferproc)mmap_buffer_releasebuf,
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +0000996};
997
Georg Brandl86def6c2008-01-21 20:36:10 +0000998static PyObject *
999new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1000
Christian Heimese1c98112008-01-21 11:20:28 +00001001PyDoc_STRVAR(mmap_doc,
1002"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1003\n\
1004Maps length bytes from the file specified by the file handle fileno,\n\
1005and returns a mmap object. If length is larger than the current size\n\
1006of the file, the file is extended to contain length bytes. If length\n\
1007is 0, the maximum length of the map is the current size of the file,\n\
1008except that if the file is empty Windows raises an exception (you cannot\n\
1009create an empty mapping on Windows).\n\
1010\n\
1011Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1012\n\
1013Maps length bytes from the file specified by the file descriptor fileno,\n\
1014and returns a mmap object. If length is 0, the maximum length of the map\n\
1015will be the current size of the file when mmap is called.\n\
1016flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1017private copy-on-write mapping, so changes to the contents of the mmap\n\
Benjamin Petersonc4fe6f32008-08-19 18:57:56 +00001018object will be private to this process, and MAP_SHARED creates a mapping\n\
Christian Heimese1c98112008-01-21 11:20:28 +00001019that's shared with all other processes mapping the same areas of the file.\n\
1020The default value is MAP_SHARED.\n\
1021\n\
1022To map anonymous memory, pass -1 as the fileno (both versions).");
1023
1024
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001025static PyTypeObject mmap_object_type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001026 PyVarObject_HEAD_INIT(NULL, 0)
1027 "mmap.mmap", /* tp_name */
1028 sizeof(mmap_object), /* tp_size */
1029 0, /* tp_itemsize */
1030 /* methods */
1031 (destructor) mmap_object_dealloc, /* tp_dealloc */
1032 0, /* tp_print */
1033 0, /* tp_getattr */
1034 0, /* tp_setattr */
1035 0, /* tp_reserved */
1036 0, /* tp_repr */
1037 0, /* tp_as_number */
1038 &mmap_as_sequence, /*tp_as_sequence*/
1039 &mmap_as_mapping, /*tp_as_mapping*/
1040 0, /*tp_hash*/
1041 0, /*tp_call*/
1042 0, /*tp_str*/
1043 PyObject_GenericGetAttr, /*tp_getattro*/
1044 0, /*tp_setattro*/
1045 &mmap_as_buffer, /*tp_as_buffer*/
Stefan Krah23186992012-03-06 15:37:36 +01001046 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001047 mmap_doc, /*tp_doc*/
1048 0, /* tp_traverse */
1049 0, /* tp_clear */
1050 0, /* tp_richcompare */
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001051 offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001052 0, /* tp_iter */
1053 0, /* tp_iternext */
1054 mmap_object_methods, /* tp_methods */
1055 0, /* tp_members */
Georg Brandl0bccc182010-08-01 14:50:00 +00001056 mmap_object_getset, /* tp_getset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001057 0, /* tp_base */
1058 0, /* tp_dict */
1059 0, /* tp_descr_get */
1060 0, /* tp_descr_set */
1061 0, /* tp_dictoffset */
Stefan Krah23186992012-03-06 15:37:36 +01001062 0, /* tp_init */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001063 PyType_GenericAlloc, /* tp_alloc */
1064 new_mmap_object, /* tp_new */
Stefan Krah23186992012-03-06 15:37:36 +01001065 PyObject_Del, /* tp_free */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001066};
1067
Andrew M. Kuchling70d27422000-06-18 04:45:14 +00001068
Tim Petersec0a5f02006-02-16 23:47:20 +00001069#ifdef UNIX
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001070#ifdef HAVE_LARGEFILE_SUPPORT
1071#define _Py_PARSE_OFF_T "L"
1072#else
1073#define _Py_PARSE_OFF_T "l"
1074#endif
1075
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001076static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001077new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001078{
Victor Stinnere134a7f2015-03-30 10:09:31 +02001079 struct _Py_stat_struct status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001080 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001081 Py_ssize_t map_size;
1082 off_t offset = 0;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001083 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1084 int devzero = -1;
1085 int access = (int)ACCESS_DEFAULT;
1086 static char *keywords[] = {"fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001087 "flags", "prot",
1088 "access", "offset", NULL};
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001089
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001090 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1091 &fd, &map_size, &flags, &prot,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001092 &access, &offset))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001093 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001094 if (map_size < 0) {
1095 PyErr_SetString(PyExc_OverflowError,
1096 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001097 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001098 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001099 if (offset < 0) {
1100 PyErr_SetString(PyExc_OverflowError,
1101 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001102 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001103 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001104
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001105 if ((access != (int)ACCESS_DEFAULT) &&
1106 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1107 return PyErr_Format(PyExc_ValueError,
1108 "mmap can't specify both access and flags, prot.");
1109 switch ((access_mode)access) {
1110 case ACCESS_READ:
1111 flags = MAP_SHARED;
1112 prot = PROT_READ;
1113 break;
1114 case ACCESS_WRITE:
1115 flags = MAP_SHARED;
1116 prot = PROT_READ | PROT_WRITE;
1117 break;
1118 case ACCESS_COPY:
1119 flags = MAP_PRIVATE;
1120 prot = PROT_READ | PROT_WRITE;
1121 break;
1122 case ACCESS_DEFAULT:
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +01001123 /* map prot to access type */
1124 if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1125 /* ACCESS_DEFAULT */
1126 }
1127 else if (prot & PROT_WRITE) {
1128 access = ACCESS_WRITE;
1129 }
1130 else {
1131 access = ACCESS_READ;
1132 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001133 break;
1134 default:
1135 return PyErr_Format(PyExc_ValueError,
1136 "mmap invalid access parameter.");
1137 }
Neal Norwitzb5673922002-09-05 21:48:07 +00001138
Victor Stinnera6cd0cf2011-05-02 01:05:37 +02001139#ifdef __APPLE__
1140 /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1141 fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1142 if (fd != -1)
1143 (void)fcntl(fd, F_FULLFSYNC);
1144#endif
Victor Stinnere134a7f2015-03-30 10:09:31 +02001145 if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1146 && S_ISREG(status.st_mode)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001147 if (map_size == 0) {
Victor Stinnere134a7f2015-03-30 10:09:31 +02001148 if (status.st_size == 0) {
Jesus Cea941bfcc2012-09-10 00:27:55 +02001149 PyErr_SetString(PyExc_ValueError,
1150 "cannot mmap an empty file");
1151 return NULL;
1152 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001153 if (offset >= status.st_size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001154 PyErr_SetString(PyExc_ValueError,
1155 "mmap offset is greater than file size");
1156 return NULL;
1157 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001158 if (status.st_size - offset > PY_SSIZE_T_MAX) {
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001159 PyErr_SetString(PyExc_ValueError,
1160 "mmap length is too large");
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001161 return NULL;
1162 }
Victor Stinnere134a7f2015-03-30 10:09:31 +02001163 map_size = (Py_ssize_t) (status.st_size - offset);
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001164 } else if (offset > status.st_size || status.st_size - offset < map_size) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001165 PyErr_SetString(PyExc_ValueError,
1166 "mmap length is greater than file size");
1167 return NULL;
1168 }
1169 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001170 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1171 if (m_obj == NULL) {return NULL;}
1172 m_obj->data = NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001173 m_obj->size = map_size;
1174 m_obj->pos = 0;
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001175 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001176 m_obj->exports = 0;
1177 m_obj->offset = offset;
1178 if (fd == -1) {
1179 m_obj->fd = -1;
1180 /* Assume the caller wants to map anonymous memory.
1181 This is the same behaviour as Windows. mmap.mmap(-1, size)
1182 on both Windows and Unix map anonymous memory.
1183 */
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001184#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001185 /* BSD way to map anonymous memory */
1186 flags |= MAP_ANONYMOUS;
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001187#else
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001188 /* SVR4 method to map anonymous memory is to open /dev/zero */
Victor Stinnerdaf45552013-08-28 00:53:59 +02001189 fd = devzero = _Py_open("/dev/zero", O_RDWR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001190 if (devzero == -1) {
1191 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001192 return NULL;
1193 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001194#endif
Victor Stinnerdaf45552013-08-28 00:53:59 +02001195 }
1196 else {
1197 m_obj->fd = _Py_dup(fd);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001198 if (m_obj->fd == -1) {
1199 Py_DECREF(m_obj);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001200 return NULL;
1201 }
1202 }
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001203
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001204 m_obj->data = mmap(NULL, map_size,
1205 prot, flags,
1206 fd, offset);
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001207
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001208 if (devzero != -1) {
1209 close(devzero);
1210 }
1211
1212 if (m_obj->data == (char *)-1) {
1213 m_obj->data = NULL;
1214 Py_DECREF(m_obj);
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001215 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001216 return NULL;
1217 }
1218 m_obj->access = (access_mode)access;
1219 return (PyObject *)m_obj;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001220}
1221#endif /* UNIX */
1222
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001223#ifdef MS_WINDOWS
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001224
1225/* A note on sizes and offsets: while the actual map size must hold in a
1226 Py_ssize_t, both the total file size and the start offset can be longer
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001227 than a Py_ssize_t, so we use long long which is always 64-bit.
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001228*/
1229
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001230static PyObject *
Georg Brandl86def6c2008-01-21 20:36:10 +00001231new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001232{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001233 mmap_object *m_obj;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001234 Py_ssize_t map_size;
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001235 long long offset = 0, size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001236 DWORD off_hi; /* upper 32 bits of offset */
1237 DWORD off_lo; /* lower 32 bits of offset */
1238 DWORD size_hi; /* upper 32 bits of size */
1239 DWORD size_lo; /* lower 32 bits of size */
1240 char *tagname = "";
1241 DWORD dwErr = 0;
1242 int fileno;
1243 HANDLE fh = 0;
1244 int access = (access_mode)ACCESS_DEFAULT;
1245 DWORD flProtect, dwDesiredAccess;
1246 static char *keywords[] = { "fileno", "length",
Stefan Krah23186992012-03-06 15:37:36 +01001247 "tagname",
1248 "access", "offset", NULL };
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001249
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001250 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1251 &fileno, &map_size,
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001252 &tagname, &access, &offset)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001253 return NULL;
1254 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001255
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001256 switch((access_mode)access) {
1257 case ACCESS_READ:
1258 flProtect = PAGE_READONLY;
1259 dwDesiredAccess = FILE_MAP_READ;
1260 break;
1261 case ACCESS_DEFAULT: case ACCESS_WRITE:
1262 flProtect = PAGE_READWRITE;
1263 dwDesiredAccess = FILE_MAP_WRITE;
1264 break;
1265 case ACCESS_COPY:
1266 flProtect = PAGE_WRITECOPY;
1267 dwDesiredAccess = FILE_MAP_COPY;
1268 break;
1269 default:
1270 return PyErr_Format(PyExc_ValueError,
1271 "mmap invalid access parameter.");
1272 }
Tim Peters5ebfd362001-11-13 23:11:19 +00001273
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001274 if (map_size < 0) {
1275 PyErr_SetString(PyExc_OverflowError,
1276 "memory mapped length must be postiive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001277 return NULL;
Benjamin Petersoncd04db02016-10-05 21:45:48 -07001278 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001279 if (offset < 0) {
1280 PyErr_SetString(PyExc_OverflowError,
1281 "memory mapped offset must be positive");
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001282 return NULL;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001283 }
Tim Petersec0a5f02006-02-16 23:47:20 +00001284
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001285 /* assume -1 and 0 both mean invalid filedescriptor
1286 to 'anonymously' map memory.
1287 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1288 XXX: Should this code be added?
1289 if (fileno == 0)
1290 PyErr_WarnEx(PyExc_DeprecationWarning,
1291 "don't use 0 for anonymous memory",
1292 1);
1293 */
1294 if (fileno != -1 && fileno != 0) {
Brian Curtinea47eaa2010-08-01 15:26:26 +00001295 /* Ensure that fileno is within the CRT's valid range */
Steve Dower8fc89802015-04-12 00:26:27 -04001296 _Py_BEGIN_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001297 fh = (HANDLE)_get_osfhandle(fileno);
Steve Dower8fc89802015-04-12 00:26:27 -04001298 _Py_END_SUPPRESS_IPH
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001299 if (fh==(HANDLE)-1) {
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001300 PyErr_SetFromErrno(PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001301 return NULL;
1302 }
1303 /* Win9x appears to need us seeked to zero */
1304 lseek(fileno, 0, SEEK_SET);
1305 }
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001306
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001307 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1308 if (m_obj == NULL)
1309 return NULL;
1310 /* Set every field to an invalid marker, so we can safely
1311 destruct the object in the face of failure */
1312 m_obj->data = NULL;
1313 m_obj->file_handle = INVALID_HANDLE_VALUE;
1314 m_obj->map_handle = NULL;
1315 m_obj->tagname = NULL;
1316 m_obj->offset = offset;
Mark Hammond2cbed002000-07-30 02:22:43 +00001317
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001318 if (fh) {
1319 /* It is necessary to duplicate the handle, so the
1320 Python code can close it on us */
1321 if (!DuplicateHandle(
1322 GetCurrentProcess(), /* source process handle */
1323 fh, /* handle to be duplicated */
1324 GetCurrentProcess(), /* target proc handle */
1325 (LPHANDLE)&m_obj->file_handle, /* result */
1326 0, /* access - ignored due to options value */
1327 FALSE, /* inherited by child processes? */
1328 DUPLICATE_SAME_ACCESS)) { /* options */
1329 dwErr = GetLastError();
1330 Py_DECREF(m_obj);
1331 PyErr_SetFromWindowsErr(dwErr);
1332 return NULL;
1333 }
1334 if (!map_size) {
1335 DWORD low,high;
1336 low = GetFileSize(fh, &high);
1337 /* low might just happen to have the value INVALID_FILE_SIZE;
1338 so we need to check the last error also. */
1339 if (low == INVALID_FILE_SIZE &&
1340 (dwErr = GetLastError()) != NO_ERROR) {
1341 Py_DECREF(m_obj);
1342 return PyErr_SetFromWindowsErr(dwErr);
1343 }
Guido van Rossum98297ee2007-11-06 21:34:58 +00001344
Benjamin Petersonaf580df2016-09-06 10:46:49 -07001345 size = (((long long) high) << 32) + low;
Jesus Cea1f2799b2012-09-10 22:49:50 +02001346 if (size == 0) {
1347 PyErr_SetString(PyExc_ValueError,
1348 "cannot mmap an empty file");
Jesus Ceae8db3562012-09-10 22:58:07 +02001349 Py_DECREF(m_obj);
Jesus Cea1f2799b2012-09-10 22:49:50 +02001350 return NULL;
1351 }
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001352 if (offset >= size) {
Antoine Pitrou305bc9e2011-01-20 21:07:24 +00001353 PyErr_SetString(PyExc_ValueError,
1354 "mmap offset is greater than file size");
1355 Py_DECREF(m_obj);
1356 return NULL;
1357 }
Richard Oudkerk0d09ba82013-02-13 12:18:03 +00001358 if (size - offset > PY_SSIZE_T_MAX) {
1359 PyErr_SetString(PyExc_ValueError,
1360 "mmap length is too large");
1361 Py_DECREF(m_obj);
1362 return NULL;
1363 }
1364 m_obj->size = (Py_ssize_t) (size - offset);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001365 } else {
1366 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001367 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001368 }
1369 }
1370 else {
1371 m_obj->size = map_size;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001372 size = offset + map_size;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001373 }
Guido van Rossum09fdf072000-03-31 01:17:07 +00001374
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001375 /* set the initial position */
1376 m_obj->pos = (size_t) 0;
Guido van Rossum09fdf072000-03-31 01:17:07 +00001377
Antoine Pitrouc53204b2013-08-05 23:17:30 +02001378 m_obj->weakreflist = NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001379 m_obj->exports = 0;
1380 /* set the tag name */
1381 if (tagname != NULL && *tagname != '\0') {
1382 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1383 if (m_obj->tagname == NULL) {
1384 PyErr_NoMemory();
1385 Py_DECREF(m_obj);
1386 return NULL;
1387 }
1388 strcpy(m_obj->tagname, tagname);
1389 }
1390 else
1391 m_obj->tagname = NULL;
Mark Hammond2cbed002000-07-30 02:22:43 +00001392
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001393 m_obj->access = (access_mode)access;
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001394 size_hi = (DWORD)(size >> 32);
1395 size_lo = (DWORD)(size & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001396 off_hi = (DWORD)(offset >> 32);
1397 off_lo = (DWORD)(offset & 0xFFFFFFFF);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001398 /* For files, it would be sufficient to pass 0 as size.
1399 For anonymous maps, we have to pass the size explicitly. */
1400 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1401 NULL,
1402 flProtect,
1403 size_hi,
1404 size_lo,
1405 m_obj->tagname);
1406 if (m_obj->map_handle != NULL) {
1407 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1408 dwDesiredAccess,
1409 off_hi,
1410 off_lo,
1411 m_obj->size);
1412 if (m_obj->data != NULL)
1413 return (PyObject *)m_obj;
1414 else {
1415 dwErr = GetLastError();
1416 CloseHandle(m_obj->map_handle);
1417 m_obj->map_handle = NULL;
1418 }
1419 } else
1420 dwErr = GetLastError();
1421 Py_DECREF(m_obj);
1422 PyErr_SetFromWindowsErr(dwErr);
1423 return NULL;
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001424}
Martin v. Löwis6238d2b2002-06-30 15:26:10 +00001425#endif /* MS_WINDOWS */
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001426
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001427static void
1428setint(PyObject *d, const char *name, long value)
1429{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001430 PyObject *o = PyLong_FromLong(value);
1431 if (o && PyDict_SetItemString(d, name, o) == 0) {
1432 Py_DECREF(o);
1433 }
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001434}
1435
Martin v. Löwis1a214512008-06-11 05:26:20 +00001436
1437static struct PyModuleDef mmapmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001438 PyModuleDef_HEAD_INIT,
1439 "mmap",
1440 NULL,
1441 -1,
1442 NULL,
1443 NULL,
1444 NULL,
1445 NULL,
1446 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001447};
1448
Mark Hammond62b1ab12002-07-23 06:31:15 +00001449PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001450PyInit_mmap(void)
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001451{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001452 PyObject *dict, *module;
Tim Peters2caf8df2001-01-14 05:05:51 +00001453
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001454 if (PyType_Ready(&mmap_object_type) < 0)
1455 return NULL;
Tim Peters2caf8df2001-01-14 05:05:51 +00001456
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001457 module = PyModule_Create(&mmapmodule);
1458 if (module == NULL)
1459 return NULL;
1460 dict = PyModule_GetDict(module);
1461 if (!dict)
1462 return NULL;
Antoine Pitrou6b4883d2011-10-12 02:54:14 +02001463 PyDict_SetItemString(dict, "error", PyExc_OSError);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001464 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001465#ifdef PROT_EXEC
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001466 setint(dict, "PROT_EXEC", PROT_EXEC);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001467#endif
1468#ifdef PROT_READ
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001469 setint(dict, "PROT_READ", PROT_READ);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001470#endif
1471#ifdef PROT_WRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001472 setint(dict, "PROT_WRITE", PROT_WRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001473#endif
1474
1475#ifdef MAP_SHARED
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001476 setint(dict, "MAP_SHARED", MAP_SHARED);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001477#endif
1478#ifdef MAP_PRIVATE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001479 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001480#endif
1481#ifdef MAP_DENYWRITE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001482 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001483#endif
1484#ifdef MAP_EXECUTABLE
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001485 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001486#endif
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +00001487#ifdef MAP_ANONYMOUS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001488 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1489 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
Andrew M. Kuchling1ed7d2d2000-03-30 21:14:30 +00001490#endif
1491
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001492 setint(dict, "PAGESIZE", (long)my_getpagesize());
Andrew M. Kuchling961fe172000-06-03 19:41:42 +00001493
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001494 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001495
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001496 setint(dict, "ACCESS_READ", ACCESS_READ);
1497 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1498 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1499 return module;
Tim Peters5ebfd362001-11-13 23:11:19 +00001500}