summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libmmap.tex162
-rw-r--r--Lib/test/output/test_mmap13
-rw-r--r--Lib/test/test_mmap.py138
-rw-r--r--Misc/NEWS9
-rw-r--r--Modules/mmapmodule.c235
5 files changed, 412 insertions, 145 deletions
diff --git a/Doc/lib/libmmap.tex b/Doc/lib/libmmap.tex
index 0bd0332..917635b 100644
--- a/Doc/lib/libmmap.tex
+++ b/Doc/lib/libmmap.tex
@@ -1,5 +1,5 @@
\section{\module{mmap} ---
- Memory-mapped file support}
+Memory-mapped file support}
\declaremodule{builtin}{mmap}
\modulesynopsis{Interface to memory-mapped files for Unix and Windows.}
@@ -23,36 +23,67 @@ If you wish to map an existing Python file object, use its
\function{os.open()} function, which returns a file descriptor
directly (the file still needs to be closed when done).
-\begin{funcdesc}{mmap}{fileno, length\optional{, tagname}}
-\strong{(Windows version)} Maps \var{length} bytes from the file
-specified by the file handle \var{fileno}, and returns a mmap object.
-If \var{length} is \code{0}, the maximum length of the map will be the
-current size of the file when \function{mmap()} is called.
-
-\var{tagname}, if specified and not \code{None}, is a string giving a
-tag name for the mapping. Windows allows you to have many different
-mappings against the same file. If you specify the name of an
-existing tag, that tag is opened, otherwise a new tag of this name is
-created. If this parameter is omitted or \code{None}, the mapping is
-created without a name. Avoiding the use of the tag parameter will
-assist in keeping your code portable between \UNIX{} and Windows.
+\begin{funcdesc}{mmap}{fileno, length\optional{, tagname\optional{, access}}}
+ \strong{(Windows version)} Maps \var{length} bytes from the file
+ specified by the file handle \var{fileno}, and returns a mmap
+ object. If \var{length} is \code{0}, the maximum length of the map
+ will be the current size of the file when \function{mmap()} is
+ called.
+
+ \var{tagname}, if specified and not \code{None}, is a string giving
+ a tag name for the mapping. Windows allows you to have many
+ different mappings against the same file. If you specify the name
+ of an existing tag, that tag is opened, otherwise a new tag of this
+ name is created. If this parameter is omitted or \code{None}, the
+ mapping is created without a name. Avoiding the use of the tag
+ parameter will assist in keeping your code portable between \UNIX{}
+ and Windows.
+
+ \var{access} may be specified as an optional keyword parameter.
+ \var{access} accepts one of three values: \constant{ACCESS_READ},
+ \constant{ACCESS_WRITE}, or \constant{ACCESS_COPY} to specify
+ readonly, write-through or copy-on-write memory respectively.
+ \var{access} can be used on both \UNIX{} and Windows. If
+ \var{access} is not specified, Windows mmap returns a write-through
+ mapping. The initial memory values for all three access types are
+ taken from the specified file. Assignment to an
+ \constant{ACCESS_READ} memory map raises a \exception{TypeError}
+ exception. Assignment to an \constant{ACCESS_WRITE} memory map
+ affects both memory and the underlying file. Assigment to an
+ \constant{ACCESS_COPY} memory map affects memory but does not update
+ the underlying file.
\end{funcdesc}
-\begin{funcdesc}{mmap}{fileno, length\optional{, flags\optional{, prot}}}
-\strong{(\UNIX{} version)} Maps \var{length} bytes from the file
-specified by the file descriptor \var{fileno}, and returns a mmap object.
-
-\var{flags} specifies the nature of the mapping.
-\constant{MAP_PRIVATE} creates a private copy-on-write mapping, so
-changes to the contents of the mmap object will be private to this
-process, and \constant{MAP_SHARED} creates a mapping that's shared
-with all other processes mapping the same areas of the file.
-The default value is \constant{MAP_SHARED}.
-
-\var{prot}, if specified, gives the desired memory protection; the two
-most useful values are \constant{PROT_READ} and \constant{PROT_WRITE},
-to specify that the pages may be read or written.
-\var{prot} defaults to \constant{PROT_READ | PROT_WRITE}.
+\begin{funcdesc}{mmap}{fileno, length\optional{, flags\optional{, prot\optional{, access}}}}
+ \strong{(\UNIX{} version)} Maps \var{length} bytes from the file
+ specified by the file descriptor \var{fileno}, and returns a mmap
+ object.
+
+ \var{flags} specifies the nature of the mapping.
+ \constant{MAP_PRIVATE} creates a private copy-on-write mapping, so
+ changes to the contents of the mmap object will be private to this
+ process, and \constant{MAP_SHARED} creates a mapping that's shared
+ with all other processes mapping the same areas of the file. The
+ default value is \constant{MAP_SHARED}.
+
+ \var{prot}, if specified, gives the desired memory protection; the
+ two most useful values are \constant{PROT_READ} and
+ \constant{PROT_WRITE}, to specify that the pages may be read or
+ written. \var{prot} defaults to \constant{PROT_READ | PROT_WRITE}.
+
+ \var{access} may be specified in lieu of \var{flags} and \var{prot}
+ as an optional keyword parameter. \var{access} accepts one of three
+ values: \constant{ACCESS_READ}, \constant{ACCESS_WRITE}, or
+ \constant{ACCESS_COPY} to specify readonly, write-through, or
+ copy-on-write memory respectively. \var{access} can be used on both
+ \UNIX{} and Windows. It is an error to specify both \var{flags},
+ \var{prot} and \var{access}. The initial memory values for all
+ three access types are taken from the specified file. Assignment to
+ an \constant{ACCESS_READ} memory map raises a \exception{TypeError}
+ exception. Assignment to an \constant{ACCESS_WRITE} memory map
+ affects both memory and the underlying file. Assigment to an
+ \constant{ACCESS_COPY} memory map affects memory but does not update
+ the underlying file.
\end{funcdesc}
@@ -60,73 +91,80 @@ Memory-mapped file objects support the following methods:
\begin{methoddesc}{close}{}
-Close the file. Subsequent calls to other methods of the object
-will result in an exception being raised.
+ Close the file. Subsequent calls to other methods of the object
+ will result in an exception being raised.
\end{methoddesc}
\begin{methoddesc}{find}{string\optional{, start}}
-Returns the lowest index in the object where the substring
-\var{string} is found. Returns \code{-1} on failure. \var{start} is
-the index at which the search begins, and defaults to zero.
+ Returns the lowest index in the object where the substring
+ \var{string} is found. Returns \code{-1} on failure. \var{start}
+ is the index at which the search begins, and defaults to zero.
\end{methoddesc}
\begin{methoddesc}{flush}{\optional{offset, size}}
-Flushes changes made to the in-memory copy of a file back to disk.
-Without use of this call there is no guarantee that changes are
-written back before the object is destroyed. If \var{offset} and
-\var{size} are specified, only changes to the given range of bytes
-will be flushed to disk; otherwise, the whole extent of the mapping is
-flushed.
+ Flushes changes made to the in-memory copy of a file back to disk.
+ Without use of this call there is no guarantee that changes are
+ written back before the object is destroyed. If \var{offset} and
+ \var{size} are specified, only changes to the given range of bytes
+ will be flushed to disk; otherwise, the whole extent of the mapping
+ is flushed.
\end{methoddesc}
\begin{methoddesc}{move}{\var{dest}, \var{src}, \var{count}}
-Copy the \var{count} bytes starting at offset \var{src}
-to the destination index \var{dest}.
+ Copy the \var{count} bytes starting at offset \var{src} to the
+ destination index \var{dest}. If the mmap was created with
+ \constant{ACCESS_READ}, then calls to move will throw a
+ \exception{TypeError} exception.
\end{methoddesc}
\begin{methoddesc}{read}{\var{num}}
-Return a string containing up to \var{num} bytes starting from the
-current file position; the file position is updated to point after the
-bytes that were returned.
+ Return a string containing up to \var{num} bytes starting from the
+ current file position; the file position is updated to point after the
+ bytes that were returned.
\end{methoddesc}
\begin{methoddesc}{read_byte}{}
-Returns a string of length 1 containing the character at the current
-file position, and advances the file position by 1.
+ Returns a string of length 1 containing the character at the current
+ file position, and advances the file position by 1.
\end{methoddesc}
\begin{methoddesc}{readline}{}
-Returns a single line, starting at the current file position and up to
-the next newline.
+ Returns a single line, starting at the current file position and up to
+ the next newline.
\end{methoddesc}
\begin{methoddesc}{resize}{\var{newsize}}
+ If the mmap was created with \constant{ACCESS_READ} or
+ \constant{ACCESS_COPY}, resizing the map will throw a \exception{TypeError} exception.
\end{methoddesc}
\begin{methoddesc}{seek}{pos\optional{, whence}}
-Set the file's current position.
-\var{whence} argument is optional and defaults to \code{0} (absolute
-file positioning); other values are \code{1} (seek relative to the
-current position) and \code{2} (seek relative to the file's end).
+ Set the file's current position. \var{whence} argument is optional
+ and defaults to \code{0} (absolute file positioning); other values
+ are \code{1} (seek relative to the current position) and \code{2}
+ (seek relative to the file's end).
\end{methoddesc}
\begin{methoddesc}{size}{}
-Return the length of the file, which can be larger than the size
-of the memory-mapped area.
+ Return the length of the file, which can be larger than the size of
+ the memory-mapped area.
\end{methoddesc}
\begin{methoddesc}{tell}{}
-Returns the current position of the file pointer.
+ Returns the current position of the file pointer.
\end{methoddesc}
\begin{methoddesc}{write}{\var{string}}
-Write the bytes in \var{string} into memory at the current position of
-the file pointer; the file position is updated to point after the
-bytes that were written.
+ Write the bytes in \var{string} into memory at the current position
+ of the file pointer; the file position is updated to point after the
+ bytes that were written. If the mmap was created with
+ \constant{ACCESS_READ}, then writing to it will throw a
+ \exception{TypeError} exception.
\end{methoddesc}
\begin{methoddesc}{write_byte}{\var{byte}}
-Write the single-character string \var{byte} into memory at the
-current position of the file pointer; the file position is advanced by
-\code{1}.
+ Write the single-character string \var{byte} into memory at the
+ current position of the file pointer; the file position is advanced
+ by \code{1}.If the mmap was created with \constant{ACCESS_READ},
+ then writing to it will throw a \exception{TypeError} exception.
\end{methoddesc}
diff --git a/Lib/test/output/test_mmap b/Lib/test/output/test_mmap
index 815cfe3..f1a25a9 100644
--- a/Lib/test/output/test_mmap
+++ b/Lib/test/output/test_mmap
@@ -17,4 +17,17 @@ test_mmap
Try to seek beyond end of mmap...
Try to seek to negative position...
Attempting resize()
+ Creating 10 byte test data file.
+ Opening mmap with access=ACCESS_READ
+ Ensuring that readonly mmap can't be slice assigned.
+ Ensuring that readonly mmap can't be item assigned.
+ Ensuring that readonly mmap can't be write() to.
+ Ensuring that readonly mmap can't be write_byte() to.
+ Ensuring that readonly mmap can't be resized.
+ Opening mmap with access=ACCESS_WRITE
+ Modifying write-through memory map.
+ Opening mmap with access=ACCESS_COPY
+ Modifying copy-on-write memory map.
+ Ensuring copy-on-write maps cannot be resized.
+ Ensuring invalid access parameter raises exception.
Test passed
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index eb31dc3..6bb974e 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -1,4 +1,4 @@
-from test_support import verify, TESTFN
+from test_support import verify, vereq, TESTFN
import mmap
import os, re
@@ -25,15 +25,15 @@ def test_both():
print type(m) # SF bug 128713: segfaulted on Linux
print ' Position of foo:', m.find('foo') / float(PAGESIZE), 'pages'
- verify(m.find('foo') == PAGESIZE)
+ vereq(m.find('foo'), PAGESIZE)
print ' Length of file:', len(m) / float(PAGESIZE), 'pages'
- verify(len(m) == 2*PAGESIZE)
+ vereq(len(m), 2*PAGESIZE)
print ' Contents of byte 0:', repr(m[0])
- verify(m[0] == '\0')
+ vereq(m[0], '\0')
print ' Contents of first 3 bytes:', repr(m[0:3])
- verify(m[0:3] == '\0\0\0')
+ vereq(m[0:3], '\0\0\0')
# Modify the file's content
print "\n Modifying file's content..."
@@ -42,11 +42,11 @@ def test_both():
# Check that the modification worked
print ' Contents of byte 0:', repr(m[0])
- verify(m[0] == '3')
+ vereq(m[0], '3')
print ' Contents of first 3 bytes:', repr(m[0:3])
- verify(m[0:3] == '3\0\0')
+ vereq(m[0:3], '3\0\0')
print ' Contents of second page:', repr(m[PAGESIZE-1 : PAGESIZE + 7])
- verify(m[PAGESIZE-1 : PAGESIZE + 7] == '\0foobar\0')
+ vereq(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
m.flush()
@@ -61,19 +61,19 @@ def test_both():
print ' Regex match on mmap (page start, length of match):',
print start / float(PAGESIZE), length
- verify(start == PAGESIZE)
- verify(end == PAGESIZE + 6)
+ vereq(start, PAGESIZE)
+ vereq(end, PAGESIZE + 6)
# test seeking around (try to overflow the seek implementation)
m.seek(0,0)
print ' Seek to zeroth byte'
- verify(m.tell() == 0)
+ vereq(m.tell(), 0)
m.seek(42,1)
print ' Seek to 42nd byte'
- verify(m.tell() == 42)
+ vereq(m.tell(), 42)
m.seek(0,2)
print ' Seek to last byte'
- verify(m.tell() == len(m))
+ vereq(m.tell(), len(m))
print ' Try to seek to negative position...'
try:
@@ -132,6 +132,118 @@ def test_both():
except OSError:
pass
+ # Test for "access" keyword parameter
+ try:
+ mapsize = 10
+ print " Creating", mapsize, "byte test data file."
+ open(TESTFN, "wb").write("a"*mapsize)
+ print " Opening mmap with access=ACCESS_READ"
+ f = open(TESTFN, "rb")
+ m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
+ verify(m[:] == 'a'*mapsize, "Readonly memory map data incorrect.")
+
+ print " Ensuring that readonly mmap can't be slice assigned."
+ try:
+ m[:] = 'b'*mapsize
+ except TypeError:
+ pass
+ else:
+ verify(0, "Able to write to readonly memory map")
+
+ print " Ensuring that readonly mmap can't be item assigned."
+ try:
+ m[0] = 'b'
+ except TypeError:
+ pass
+ else:
+ verify(0, "Able to write to readonly memory map")
+
+ print " Ensuring that readonly mmap can't be write() to."
+ try:
+ m.seek(0,0)
+ m.write('abc')
+ except TypeError:
+ pass
+ else:
+ verify(0, "Able to write to readonly memory map")
+
+ print " Ensuring that readonly mmap can't be write_byte() to."
+ try:
+ m.seek(0,0)
+ m.write_byte('d')
+ except TypeError:
+ pass
+ else:
+ verify(0, "Able to write to readonly memory map")
+
+ print " Ensuring that readonly mmap can't be resized."
+ try:
+ m.resize(2*mapsize)
+ except SystemError: # resize is not universally supported
+ pass
+ except TypeError:
+ pass
+ else:
+ verify(0, "Able to resize readonly memory map")
+ del m, f
+ verify(open(TESTFN, "rb").read() == 'a'*mapsize,
+ "Readonly memory map data file was modified")
+
+ print " Opening mmap with access=ACCESS_WRITE"
+ f = open(TESTFN, "r+b")
+ m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
+ print " Modifying write-through memory map."
+ m[:] = 'c'*mapsize
+ verify(m[:] == 'c'*mapsize,
+ "Write-through memory map memory not updated properly.")
+ m.flush()
+ del m, f
+ verify(open(TESTFN).read() == 'c'*mapsize,
+ "Write-through memory map data file not updated properly.")
+
+ print " Opening mmap with access=ACCESS_COPY"
+ f = open(TESTFN, "r+b")
+ m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
+ print " Modifying copy-on-write memory map."
+ m[:] = 'd'*mapsize
+ verify(m[:] == 'd' * mapsize,
+ "Copy-on-write memory map data not written correctly.")
+ m.flush()
+ verify(open(TESTFN, "rb").read() == 'c'*mapsize,
+ "Copy-on-write test data file should not be modified.")
+ try:
+ print " Ensuring copy-on-write maps cannot be resized."
+ m.resize(2*mapsize)
+ except TypeError:
+ pass
+ else:
+ verify(0, "Copy-on-write mmap resize did not raise exception.")
+ del m, f
+ try:
+ print " Ensuring invalid access parameter raises exception."
+ f = open(TESTFN, "r+b")
+ m = mmap.mmap(f.fileno(), mapsize, access=4)
+ except ValueError:
+ pass
+ else:
+ verify(0, "Invalid access code should have raised exception.")
+
+ if os.name == "posix":
+ print " Trying incompatible flags, prot and access parameters."
+ f=open(TESTFN, "r+b")
+ try:
+ m = mmap.mmap(f.fileno(), mapsize, flags=mmap.MAP_PRIVATE,
+ prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
+ except ValueError:
+ pass
+ else:
+ verify(0, "Incompatible parameters should raise ValueError.")
+ finally:
+ try:
+ os.unlink(TESTFN)
+ except OSError:
+ pass
+
print ' Test passed'
test_both()
diff --git a/Misc/NEWS b/Misc/NEWS
index ba98321..763a114 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -36,6 +36,13 @@ Core and builtins
Extension modules
+- mmap has a new keyword argument, "access", allowing a uniform way for
+ both Windows and Unix users to create read-only, write-through and
+ copy-on-write memory mappings. This was previously possible only on
+ Unix. A new keyword argument was required to support this in a
+ uniform way because the mmap() signuatures had diverged across
+ platforms. Thanks to Jay T Miller for repairing this!
+
- By default, the gc.garbage list now contains only those instances in
unreachable cycles that have __del__ methods; in 2.1 it contained all
instances in unreachable cycles. "Instances" here has been generalized
@@ -55,7 +62,7 @@ Extension modules
Library
-- tkFileDialog exposes a Directory class and askdirectory
+- tkFileDialog exposes a Directory class and askdirectory
convenience function.
- Symbolic group names in regular expressions must be unique. For
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 8d57e90..a4ec2d0 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -29,9 +29,9 @@
static int
my_getpagesize(void)
{
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwPageSize;
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
}
#endif
@@ -49,7 +49,7 @@ my_getpagesize(void)
static int
my_getpagesize(void)
{
- return sysconf(_SC_PAGESIZE);
+ return sysconf(_SC_PAGESIZE);
}
#else
#define my_getpagesize getpagesize
@@ -62,6 +62,14 @@ my_getpagesize(void)
static PyObject *mmap_module_error;
+typedef enum
+{
+ ACCESS_DEFAULT,
+ ACCESS_READ,
+ ACCESS_WRITE,
+ ACCESS_COPY
+} access_mode;
+
typedef struct {
PyObject_HEAD
char * data;
@@ -77,8 +85,11 @@ typedef struct {
#ifdef UNIX
int fd;
#endif
+
+ access_mode access;
} mmap_object;
+
static void
mmap_object_dealloc(mmap_object *m_obj)
{
@@ -178,7 +189,7 @@ mmap_read_byte_method(mmap_object *self,
static PyObject *
mmap_read_line_method(mmap_object *self,
- PyObject *args)
+ PyObject *args)
{
char *start = self->data+self->pos;
char *eof = self->data+self->size;
@@ -236,11 +247,11 @@ mmap_find_method(mmap_object *self,
char *e = self->data + self->size;
if (start < 0)
- start += self->size;
+ start += self->size;
if (start < 0)
- start = 0;
+ start = 0;
else if ((size_t)start > self->size)
- start = self->size;
+ start = self->size;
p = self->data + start;
while (p < e) {
@@ -260,6 +271,26 @@ mmap_find_method(mmap_object *self,
}
}
+static int
+is_writeable(mmap_object *self)
+{
+ if (self->access != ACCESS_READ)
+ return 1;
+ PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
+ return 0;
+}
+
+static int
+is_resizeable(mmap_object *self)
+{
+ if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
+ return 1;
+ PyErr_Format(PyExc_TypeError,
+ "mmap can't resize a readonly or copy-on-write memory map.");
+ return 0;
+}
+
+
static PyObject *
mmap_write_method(mmap_object *self,
PyObject *args)
@@ -271,6 +302,9 @@ mmap_write_method(mmap_object *self,
if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
return(NULL);
+ if (!is_writeable(self))
+ return NULL;
+
if ((self->pos + length) > self->size) {
PyErr_SetString (PyExc_ValueError, "data out of range");
return NULL;
@@ -291,12 +325,14 @@ mmap_write_byte_method(mmap_object *self,
if (!PyArg_ParseTuple (args, "c:write_byte", &value))
return(NULL);
+ if (!is_writeable(self))
+ return NULL;
*(self->data+self->pos) = value;
self->pos += 1;
Py_INCREF (Py_None);
return (Py_None);
}
-
+
static PyObject *
mmap_size_method(mmap_object *self,
PyObject *args)
@@ -342,7 +378,8 @@ mmap_resize_method(mmap_object *self,
{
unsigned long new_size;
CHECK_VALID(NULL);
- if (!PyArg_ParseTuple (args, "l:resize", &new_size)) {
+ if (!PyArg_ParseTuple (args, "l:resize", &new_size) ||
+ !is_resizeable(self)) {
return NULL;
#ifdef MS_WIN32
} else {
@@ -386,31 +423,31 @@ mmap_resize_method(mmap_object *self,
#ifdef UNIX
#ifndef HAVE_MREMAP
-} else {
- PyErr_SetString(PyExc_SystemError,
- "mmap: resizing not available--no mremap()");
- return NULL;
+ } else {
+ PyErr_SetString(PyExc_SystemError,
+ "mmap: resizing not available--no mremap()");
+ return NULL;
#else
-} else {
- void *newmap;
+ } else {
+ void *newmap;
#ifdef MREMAP_MAYMOVE
- newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
+ newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
#else
- newmap = mremap(self->data, self->size, new_size, 0);
+ newmap = mremap(self->data, self->size, new_size, 0);
#endif
- if (newmap == (void *)-1)
- {
- PyErr_SetFromErrno(mmap_module_error);
- return NULL;
- }
- self->data = newmap;
- self->size = new_size;
- Py_INCREF(Py_None);
- return Py_None;
+ if (newmap == (void *)-1)
+ {
+ PyErr_SetFromErrno(mmap_module_error);
+ return NULL;
+ }
+ self->data = newmap;
+ self->size = new_size;
+ Py_INCREF(Py_None);
+ return Py_None;
#endif /* HAVE_MREMAP */
#endif /* UNIX */
-}
+ }
}
static PyObject *
@@ -491,7 +528,7 @@ mmap_seek_method(mmap_object *self, PyObject *args)
return (Py_None);
}
-onoutofrange:
+ onoutofrange:
PyErr_SetString (PyExc_ValueError, "seek out of range");
return NULL;
}
@@ -501,7 +538,8 @@ mmap_move_method(mmap_object *self, PyObject *args)
{
unsigned long dest, src, count;
CHECK_VALID(NULL);
- if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count)) {
+ if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) ||
+ !is_writeable(self)) {
return NULL;
} else {
/* bounds check the values */
@@ -561,6 +599,8 @@ mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
"Accessing non-existent mmap segment");
return -1;
}
+ if (!is_writeable(self))
+ return -1;
*ptr = self->data;
return self->size;
}
@@ -665,7 +705,7 @@ mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
if (v == NULL) {
PyErr_SetString(PyExc_TypeError,
- "mmap object doesn't support slice deletion");
+ "mmap object doesn't support slice deletion");
return -1;
}
if (! (PyString_Check(v)) ) {
@@ -678,6 +718,8 @@ mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
"mmap slice assignment is wrong size");
return -1;
}
+ if (!is_writeable(self))
+ return -1;
buf = PyString_AsString(v);
memcpy(self->data + ilow, buf, ihigh-ilow);
return 0;
@@ -695,14 +737,16 @@ mmap_ass_item(mmap_object *self, int i, PyObject *v)
}
if (v == NULL) {
PyErr_SetString(PyExc_TypeError,
- "mmap object doesn't support item deletion");
+ "mmap object doesn't support item deletion");
return -1;
}
if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
PyErr_SetString(PyExc_IndexError,
- "mmap assignment must be single-character string");
+ "mmap assignment must be single-character string");
return -1;
}
+ if (!is_writeable(self))
+ return -1;
buf = PyString_AsString(v);
self->data[i] = buf[0];
return 0;
@@ -792,18 +836,18 @@ _GetMapSize(PyObject *o)
}
else {
PyErr_SetString(PyExc_TypeError,
- "map size must be an integral value");
+ "map size must be an integral value");
return -1;
}
-onnegoverflow:
+ onnegoverflow:
PyErr_SetString(PyExc_OverflowError,
- "memory mapped size must be positive");
+ "memory mapped size must be positive");
return -1;
-onposoverflow:
+ onposoverflow:
PyErr_SetString(PyExc_OverflowError,
- "memory mapped size is too large (limited by C int)");
+ "memory mapped size is too large (limited by C int)");
return -1;
}
@@ -815,16 +859,42 @@ new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
PyObject *map_size_obj = NULL;
int map_size;
int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
- char *keywords[] = {"file", "size", "flags", "prot", NULL};
+ access_mode access = ACCESS_DEFAULT;
+ char *keywords[] = {"fileno", "length",
+ "flags", "prot",
+ "access", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwdict,
- "iO|ii", keywords,
- &fd, &map_size_obj, &flags, &prot)
- )
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
+ &fd, &map_size_obj, &flags, &prot, &access))
return NULL;
map_size = _GetMapSize(map_size_obj);
if (map_size < 0)
return NULL;
+
+ if ((access != ACCESS_DEFAULT) &&
+ ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ))))
+ return PyErr_Format(PyExc_ValueError,
+ "mmap can't specify both access and flags, prot.");
+ switch(access) {
+ case ACCESS_READ:
+ flags = MAP_SHARED;
+ prot = PROT_READ;
+ break;
+ case ACCESS_WRITE:
+ flags = MAP_SHARED;
+ prot = PROT_READ | PROT_WRITE;
+ break;
+ case ACCESS_COPY:
+ flags = MAP_PRIVATE;
+ prot = PROT_READ | PROT_WRITE;
+ break;
+ case ACCESS_DEFAULT:
+ /* use the specified or default values of flags and prot */
+ break;
+ default:
+ return PyErr_Format(PyExc_ValueError,
+ "mmap invalid access parameter.");
+ }
m_obj = PyObject_New (mmap_object, &mmap_object_type);
if (m_obj == NULL) {return NULL;}
@@ -834,37 +904,57 @@ new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
m_obj->data = mmap(NULL, map_size,
prot, flags,
fd, 0);
- if (m_obj->data == (char *)-1)
- {
+ if (m_obj->data == (char *)-1) {
Py_DECREF(m_obj);
PyErr_SetFromErrno(mmap_module_error);
return NULL;
}
+ m_obj->access = access;
return (PyObject *)m_obj;
}
#endif /* UNIX */
#ifdef MS_WIN32
static PyObject *
-new_mmap_object(PyObject *self, PyObject *args)
+new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
{
mmap_object *m_obj;
PyObject *map_size_obj = NULL;
int map_size;
char *tagname = "";
-
DWORD dwErr = 0;
int fileno;
HANDLE fh = 0;
-
- if (!PyArg_ParseTuple(args,
- "iO|z",
- &fileno,
- &map_size_obj,
- &tagname)
- )
+ access_mode access = ACCESS_DEFAULT;
+ DWORD flProtect, dwDesiredAccess;
+ char *keywords[] = { "fileno", "length",
+ "tagname",
+ "access", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
+ &fileno, &map_size_obj,
+ &tagname, &access)) {
return NULL;
-
+ }
+
+ switch(access) {
+ case ACCESS_READ:
+ flProtect = PAGE_READONLY;
+ dwDesiredAccess = FILE_MAP_READ;
+ break;
+ case ACCESS_DEFAULT: case ACCESS_WRITE:
+ flProtect = PAGE_READWRITE;
+ dwDesiredAccess = FILE_MAP_WRITE;
+ break;
+ case ACCESS_COPY:
+ flProtect = PAGE_WRITECOPY;
+ dwDesiredAccess = FILE_MAP_COPY;
+ break;
+ default:
+ return PyErr_Format(PyExc_ValueError,
+ "mmap invalid access parameter.");
+ }
+
map_size = _GetMapSize(map_size_obj);
if (map_size < 0)
return NULL;
@@ -873,8 +963,8 @@ new_mmap_object(PyObject *self, PyObject *args)
if (fileno != 0) {
fh = (HANDLE)_get_osfhandle(fileno);
if (fh==(HANDLE)-1) {
- PyErr_SetFromErrno(mmap_module_error);
- return NULL;
+ PyErr_SetFromErrno(mmap_module_error);
+ return NULL;
}
/* Win9x appears to need us seeked to zero */
fseek(&_iob[fileno], 0, SEEK_SET);
@@ -894,13 +984,13 @@ new_mmap_object(PyObject *self, PyObject *args)
/* It is necessary to duplicate the handle, so the
Python code can close it on us */
if (!DuplicateHandle(
- GetCurrentProcess(), /* source process handle */
- fh, /* handle to be duplicated */
- GetCurrentProcess(), /* target proc handle */
- (LPHANDLE)&m_obj->file_handle, /* result */
- 0, /* access - ignored due to options value */
- FALSE, /* inherited by child processes? */
- DUPLICATE_SAME_ACCESS)) { /* options */
+ GetCurrentProcess(), /* source process handle */
+ fh, /* handle to be duplicated */
+ GetCurrentProcess(), /* target proc handle */
+ (LPHANDLE)&m_obj->file_handle, /* result */
+ 0, /* access - ignored due to options value */
+ FALSE, /* inherited by child processes? */
+ DUPLICATE_SAME_ACCESS)) { /* options */
dwErr = GetLastError();
Py_DECREF(m_obj);
PyErr_SetFromWindowsErr(dwErr);
@@ -932,22 +1022,23 @@ new_mmap_object(PyObject *self, PyObject *args)
else
m_obj->tagname = NULL;
+ m_obj->access = access;
m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
NULL,
- PAGE_READWRITE,
+ flProtect,
0,
m_obj->size,
m_obj->tagname);
if (m_obj->map_handle != NULL) {
m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
- FILE_MAP_WRITE,
+ dwDesiredAccess,
0,
0,
0);
if (m_obj->data != NULL) {
return ((PyObject *) m_obj);
} else {
- dwErr = GetLastError();
+ dwErr = GetLastError();
}
} else {
dwErr = GetLastError();
@@ -966,7 +1057,7 @@ static struct PyMethodDef mmap_functions[] = {
};
DL_EXPORT(void)
-initmmap(void)
+ initmmap(void)
{
PyObject *dict, *module;
@@ -1011,5 +1102,11 @@ initmmap(void)
PyDict_SetItemString (dict, "PAGESIZE",
PyInt_FromLong( (long)my_getpagesize() ) );
-}
+ PyDict_SetItemString (dict, "ACCESS_READ",
+ PyInt_FromLong(ACCESS_READ));
+ PyDict_SetItemString (dict, "ACCESS_WRITE",
+ PyInt_FromLong(ACCESS_WRITE));
+ PyDict_SetItemString (dict, "ACCESS_COPY",
+ PyInt_FromLong(ACCESS_COPY));
+}