Skip to content

Commit

Permalink
Fix mmap.resize errors (#1872)
Browse files Browse the repository at this point in the history
* Fix mmap.resize errors

* Fix error on Mono

* Fix mmap closing
  • Loading branch information
BCSharp authored Jan 15, 2025
1 parent 0b605ec commit 1a90cb4
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 10 deletions.
24 changes: 14 additions & 10 deletions Src/IronPython.Modules/mmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,10 @@ public void resize(long newsize) {
if (_handle.IsInvalid) {
throw PythonNT.GetOsError(PythonErrno.EBADF);
}
if (_view.Capacity == newsize) {
// resizing to the same size
return;
}
if (newsize == 0) {
// resizing to an empty mapped region is not allowed
throw PythonNT.GetOsError(PythonErrno.EINVAL);
Expand Down Expand Up @@ -844,14 +848,6 @@ public void resize(long newsize) {
throw WindowsError(PythonExceptions._OSError.ERROR_INVALID_PARAMETER);
}

if (newsize == 0) {
// resizing to an empty mapped region is not allowed
throw WindowsError(_offset == 0
? PythonExceptions._OSError.ERROR_ACCESS_DENIED
: PythonExceptions._OSError.ERROR_FILE_INVALID
);
}

if (_view.Capacity == newsize) {
// resizing to the same size
return;
Expand All @@ -860,6 +856,14 @@ public void resize(long newsize) {
long capacity = checked(_offset + newsize);

try {
if (newsize == 0) {
// resizing to an empty mapped region is not allowed
throw WindowsError(_offset != 0 && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? PythonExceptions._OSError.ERROR_ACCESS_DENIED
: PythonExceptions._OSError.ERROR_FILE_INVALID
);
}

_view.Flush();
_view.Dispose();
_file.Dispose();
Expand Down Expand Up @@ -1143,13 +1147,13 @@ private void EnsureOpen() {
}
}

private struct MmapLocker : IDisposable {
private readonly struct MmapLocker : IDisposable {
private readonly MmapDefault _mmap;

public MmapLocker(MmapDefault mmap) {
_mmap = mmap;
Interlocked.Increment(ref _mmap._refCount);
_mmap.EnsureOpen();
Interlocked.Increment(ref _mmap._refCount);
}

#region IDisposable Members
Expand Down
105 changes: 105 additions & 0 deletions Tests/modules/io_related/test_mmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Licensed to the .NET Foundation under one or more agreements.
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
# See the LICENSE file in the project root for more information.

'''
Tests the _mmap standard module.
'''

import sys
import os
import errno
import mmap

from iptest import IronPythonTestCase, is_cli, is_posix, is_windows, run_test

class MmapTest(IronPythonTestCase):

def setUp(self):
super(MmapTest, self).setUp()

self.temp_file = os.path.join(self.temporary_dir, "temp_mmap_%d.dat" % os.getpid())

def tearDown(self):
self.delete_files(self.temp_file)
return super().tearDown()


def test_constants(self):
self.assertTrue(hasattr(mmap, "PAGESIZE"))
self.assertTrue(hasattr(mmap, "ALLOCATIONGRANULARITY"))

self.assertEqual(mmap.ACCESS_READ, 1)
self.assertEqual(mmap.ACCESS_WRITE, 2)
self.assertEqual(mmap.ACCESS_COPY, 3)
if sys.version_info >= (3, 7) or is_cli:
self.assertEqual(mmap.ACCESS_DEFAULT, 0)
self.assertFalse(hasattr(mmap, "ACCESS_NONE"))

if is_posix:
self.assertEqual(mmap.MAP_SHARED, 1)
self.assertEqual(mmap.MAP_PRIVATE, 2)
self.assertEqual(mmap.PROT_READ, 1)
self.assertEqual(mmap.PROT_WRITE, 2)
self.assertEqual(mmap.PROT_EXEC, 4)


def test_resize_errors(self):
with open(self.temp_file, "wb+") as f:
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)

with open(self.temp_file, "rb+") as f:
m = mmap.mmap(f.fileno(), 0, offset=0)
with self.assertRaises(OSError) as cm:
m.resize(0)

self.assertEqual(cm.exception.errno, errno.EINVAL) # 22
if is_windows:
self.assertEqual(cm.exception.winerror, 1006) # ERROR_FILE_INVALID
self.assertEqual(cm.exception.strerror, "The volume for a file has been externally altered so that the opened file is no longer valid")
else:
self.assertEqual(cm.exception.strerror, "Invalid argument")

self.assertTrue(m.closed)


def test_resize_errors_negative(self):
with open(self.temp_file, "wb+") as f:
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)

with open(self.temp_file, "rb+") as f:
m = mmap.mmap(f.fileno(), 0, offset=0)
if is_cli or sys.version_info >= (3, 5):
self.assertRaises(ValueError, m.resize, -1)
self.assertFalse(m.closed)
else:
self.assertRaises(OSError, m.resize, -1)
self.assertTrue(m.closed)

m.close()


def test_resize_errors_offset(self):
with open(self.temp_file, "wb+") as f:
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)

with open(self.temp_file, "rb+") as f:
m = mmap.mmap(f.fileno(), 0, offset=mmap.ALLOCATIONGRANULARITY)

if is_windows:
with self.assertRaises(PermissionError) as cm:
m.resize(0)
self.assertEqual(cm.exception.errno, errno.EACCES) # 13
self.assertEqual(cm.exception.winerror, 5) # ERROR_ACCESS_DENIED
self.assertEqual(cm.exception.strerror, "Access is denied")
else:
with self.assertRaises(OSError) as cm:
m.resize(0)
self.assertEqual(cm.exception.errno, errno.EINVAL) # 22
self.assertEqual(cm.exception.strerror, "Invalid argument")

self.assertTrue(m.closed)


run_test(__name__)

0 comments on commit 1a90cb4

Please sign in to comment.