Skip to content

Commit 1a90cb4

Browse files
authored
Fix mmap.resize errors (#1872)
* Fix mmap.resize errors * Fix error on Mono * Fix mmap closing
1 parent 0b605ec commit 1a90cb4

File tree

2 files changed

+119
-10
lines changed

2 files changed

+119
-10
lines changed

Src/IronPython.Modules/mmap.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,10 @@ public void resize(long newsize) {
812812
if (_handle.IsInvalid) {
813813
throw PythonNT.GetOsError(PythonErrno.EBADF);
814814
}
815+
if (_view.Capacity == newsize) {
816+
// resizing to the same size
817+
return;
818+
}
815819
if (newsize == 0) {
816820
// resizing to an empty mapped region is not allowed
817821
throw PythonNT.GetOsError(PythonErrno.EINVAL);
@@ -844,14 +848,6 @@ public void resize(long newsize) {
844848
throw WindowsError(PythonExceptions._OSError.ERROR_INVALID_PARAMETER);
845849
}
846850

847-
if (newsize == 0) {
848-
// resizing to an empty mapped region is not allowed
849-
throw WindowsError(_offset == 0
850-
? PythonExceptions._OSError.ERROR_ACCESS_DENIED
851-
: PythonExceptions._OSError.ERROR_FILE_INVALID
852-
);
853-
}
854-
855851
if (_view.Capacity == newsize) {
856852
// resizing to the same size
857853
return;
@@ -860,6 +856,14 @@ public void resize(long newsize) {
860856
long capacity = checked(_offset + newsize);
861857

862858
try {
859+
if (newsize == 0) {
860+
// resizing to an empty mapped region is not allowed
861+
throw WindowsError(_offset != 0 && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
862+
? PythonExceptions._OSError.ERROR_ACCESS_DENIED
863+
: PythonExceptions._OSError.ERROR_FILE_INVALID
864+
);
865+
}
866+
863867
_view.Flush();
864868
_view.Dispose();
865869
_file.Dispose();
@@ -1143,13 +1147,13 @@ private void EnsureOpen() {
11431147
}
11441148
}
11451149

1146-
private struct MmapLocker : IDisposable {
1150+
private readonly struct MmapLocker : IDisposable {
11471151
private readonly MmapDefault _mmap;
11481152

11491153
public MmapLocker(MmapDefault mmap) {
11501154
_mmap = mmap;
1151-
Interlocked.Increment(ref _mmap._refCount);
11521155
_mmap.EnsureOpen();
1156+
Interlocked.Increment(ref _mmap._refCount);
11531157
}
11541158

11551159
#region IDisposable Members
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Licensed to the .NET Foundation under one or more agreements.
2+
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
# See the LICENSE file in the project root for more information.
4+
5+
'''
6+
Tests the _mmap standard module.
7+
'''
8+
9+
import sys
10+
import os
11+
import errno
12+
import mmap
13+
14+
from iptest import IronPythonTestCase, is_cli, is_posix, is_windows, run_test
15+
16+
class MmapTest(IronPythonTestCase):
17+
18+
def setUp(self):
19+
super(MmapTest, self).setUp()
20+
21+
self.temp_file = os.path.join(self.temporary_dir, "temp_mmap_%d.dat" % os.getpid())
22+
23+
def tearDown(self):
24+
self.delete_files(self.temp_file)
25+
return super().tearDown()
26+
27+
28+
def test_constants(self):
29+
self.assertTrue(hasattr(mmap, "PAGESIZE"))
30+
self.assertTrue(hasattr(mmap, "ALLOCATIONGRANULARITY"))
31+
32+
self.assertEqual(mmap.ACCESS_READ, 1)
33+
self.assertEqual(mmap.ACCESS_WRITE, 2)
34+
self.assertEqual(mmap.ACCESS_COPY, 3)
35+
if sys.version_info >= (3, 7) or is_cli:
36+
self.assertEqual(mmap.ACCESS_DEFAULT, 0)
37+
self.assertFalse(hasattr(mmap, "ACCESS_NONE"))
38+
39+
if is_posix:
40+
self.assertEqual(mmap.MAP_SHARED, 1)
41+
self.assertEqual(mmap.MAP_PRIVATE, 2)
42+
self.assertEqual(mmap.PROT_READ, 1)
43+
self.assertEqual(mmap.PROT_WRITE, 2)
44+
self.assertEqual(mmap.PROT_EXEC, 4)
45+
46+
47+
def test_resize_errors(self):
48+
with open(self.temp_file, "wb+") as f:
49+
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)
50+
51+
with open(self.temp_file, "rb+") as f:
52+
m = mmap.mmap(f.fileno(), 0, offset=0)
53+
with self.assertRaises(OSError) as cm:
54+
m.resize(0)
55+
56+
self.assertEqual(cm.exception.errno, errno.EINVAL) # 22
57+
if is_windows:
58+
self.assertEqual(cm.exception.winerror, 1006) # ERROR_FILE_INVALID
59+
self.assertEqual(cm.exception.strerror, "The volume for a file has been externally altered so that the opened file is no longer valid")
60+
else:
61+
self.assertEqual(cm.exception.strerror, "Invalid argument")
62+
63+
self.assertTrue(m.closed)
64+
65+
66+
def test_resize_errors_negative(self):
67+
with open(self.temp_file, "wb+") as f:
68+
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)
69+
70+
with open(self.temp_file, "rb+") as f:
71+
m = mmap.mmap(f.fileno(), 0, offset=0)
72+
if is_cli or sys.version_info >= (3, 5):
73+
self.assertRaises(ValueError, m.resize, -1)
74+
self.assertFalse(m.closed)
75+
else:
76+
self.assertRaises(OSError, m.resize, -1)
77+
self.assertTrue(m.closed)
78+
79+
m.close()
80+
81+
82+
def test_resize_errors_offset(self):
83+
with open(self.temp_file, "wb+") as f:
84+
f.write(b"x" * mmap.ALLOCATIONGRANULARITY * 2)
85+
86+
with open(self.temp_file, "rb+") as f:
87+
m = mmap.mmap(f.fileno(), 0, offset=mmap.ALLOCATIONGRANULARITY)
88+
89+
if is_windows:
90+
with self.assertRaises(PermissionError) as cm:
91+
m.resize(0)
92+
self.assertEqual(cm.exception.errno, errno.EACCES) # 13
93+
self.assertEqual(cm.exception.winerror, 5) # ERROR_ACCESS_DENIED
94+
self.assertEqual(cm.exception.strerror, "Access is denied")
95+
else:
96+
with self.assertRaises(OSError) as cm:
97+
m.resize(0)
98+
self.assertEqual(cm.exception.errno, errno.EINVAL) # 22
99+
self.assertEqual(cm.exception.strerror, "Invalid argument")
100+
101+
self.assertTrue(m.closed)
102+
103+
104+
run_test(__name__)
105+

0 commit comments

Comments
 (0)