Skip to content

Commit 18ee1e9

Browse files
bpo-45767: Fix integer conversion of device numbers.
Fix os.major(), os.minor() and os.makedev(). Support device numbers larger than 2**63-1. Support non-existent device number (NODEV).
1 parent de554d6 commit 18ee1e9

File tree

4 files changed

+79
-39
lines changed

4 files changed

+79
-39
lines changed

Lib/test/test_posix.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ def test_mknod(self):
691691

692692
@unittest.skipUnless(hasattr(posix, 'makedev'), 'test needs posix.makedev()')
693693
def test_makedev(self):
694+
NODEV = -1
694695
st = posix.stat(os_helper.TESTFN)
695696
dev = st.st_dev
696697
self.assertIsInstance(dev, int)
@@ -702,21 +703,28 @@ def test_makedev(self):
702703
self.assertEqual(posix.major(dev), major)
703704
self.assertRaises(TypeError, posix.major, float(dev))
704705
self.assertRaises(TypeError, posix.major)
705-
self.assertRaises((ValueError, OverflowError), posix.major, -1)
706+
for x in -2, 2**64, -2**63-1:
707+
self.assertRaises((ValueError, OverflowError), posix.major, x)
708+
self.assertEqual(posix.major(NODEV), NODEV)
706709

707710
minor = posix.minor(dev)
708711
self.assertIsInstance(minor, int)
709712
self.assertGreaterEqual(minor, 0)
710713
self.assertEqual(posix.minor(dev), minor)
711714
self.assertRaises(TypeError, posix.minor, float(dev))
712715
self.assertRaises(TypeError, posix.minor)
713-
self.assertRaises((ValueError, OverflowError), posix.minor, -1)
716+
for x in -2, 2**64, -2**63-1:
717+
self.assertRaises((ValueError, OverflowError), posix.minor, x)
718+
self.assertEqual(posix.minor(NODEV), NODEV)
714719

715720
self.assertEqual(posix.makedev(major, minor), dev)
716721
self.assertRaises(TypeError, posix.makedev, float(major), minor)
717722
self.assertRaises(TypeError, posix.makedev, major, float(minor))
718723
self.assertRaises(TypeError, posix.makedev, major)
719724
self.assertRaises(TypeError, posix.makedev)
725+
for x in -2, 2**32, 2**64, -2**63-1:
726+
self.assertRaises((ValueError, OverflowError), posix.makedev, x, minor)
727+
self.assertRaises((ValueError, OverflowError), posix.makedev, major, x)
720728

721729
def _test_all_chown_common(self, chown_func, first_param, stat_func):
722730
"""Common code for chown, fchown and lchown tests."""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix integer conversion in :func:`os.major`, :func:`os.minor`, and
2+
:func:`os.makedev`. Support device numbers larger than ``2**63-1``. Support
3+
non-existent device number.

Modules/clinic/posixmodule.c.h

Lines changed: 10 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -877,16 +877,42 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p)
877877
#endif /* MS_WINDOWS */
878878

879879

880-
#define _PyLong_FromDev PyLong_FromLongLong
880+
static PyObject *
881+
_PyLong_FromDev(dev_t dev)
882+
{
883+
if (dev == NODEV) {
884+
return PyLong_FromLongLong((long long)dev);
885+
}
886+
return PyLong_FromUnsignedLongLong((unsigned long long)dev);
887+
}
881888

882889

883890
#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS)
884891
static int
885892
_Py_Dev_Converter(PyObject *obj, void *p)
886893
{
887-
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
888-
if (PyErr_Occurred())
894+
if (PyLong_Check(obj) && Py_SIZE(obj) < 0) {
895+
int overflow;
896+
long long result = PyLong_AsLongLongAndOverflow(obj, &overflow);
897+
if (result == -1 && PyErr_Occurred()) {
898+
return 0;
899+
}
900+
if (!overflow && result == (long long)NODEV) {
901+
*((dev_t *)p) = NODEV;
902+
return 1;
903+
}
904+
}
905+
906+
unsigned long long result = PyLong_AsUnsignedLongLong(obj);
907+
if (result == (unsigned long long)-1 && PyErr_Occurred()) {
889908
return 0;
909+
}
910+
if ((unsigned long long)(dev_t)result != result) {
911+
PyErr_SetString(PyExc_OverflowError,
912+
"Python int too large to convert to C dev_t");
913+
return 0;
914+
}
915+
*((dev_t *)p) = (dev_t)result;
890916
return 1;
891917
}
892918
#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */
@@ -10638,53 +10664,68 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1063810664

1063910665
#ifdef HAVE_DEVICE_MACROS
1064010666
/*[clinic input]
10641-
os.major -> unsigned_int
10667+
os.major
1064210668
1064310669
device: dev_t
1064410670
/
1064510671
1064610672
Extracts a device major number from a raw device number.
1064710673
[clinic start generated code]*/
1064810674

10649-
static unsigned int
10675+
static PyObject *
1065010676
os_major_impl(PyObject *module, dev_t device)
10651-
/*[clinic end generated code: output=5b3b2589bafb498e input=1e16a4d30c4d4462]*/
10677+
/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/
1065210678
{
10653-
return major(device);
10679+
unsigned int result = major(device);
10680+
if (result == (unsigned int)NODEV) {
10681+
return PyLong_FromLong((int)NODEV);
10682+
}
10683+
return PyLong_FromUnsignedLong(result);
1065410684
}
1065510685

1065610686

1065710687
/*[clinic input]
10658-
os.minor -> unsigned_int
10688+
os.minor
1065910689
1066010690
device: dev_t
1066110691
/
1066210692
1066310693
Extracts a device minor number from a raw device number.
1066410694
[clinic start generated code]*/
1066510695

10666-
static unsigned int
10696+
static PyObject *
1066710697
os_minor_impl(PyObject *module, dev_t device)
10668-
/*[clinic end generated code: output=5e1a25e630b0157d input=0842c6d23f24c65e]*/
10698+
/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/
1066910699
{
10670-
return minor(device);
10700+
unsigned int result = minor(device);
10701+
if (result == (unsigned int)NODEV) {
10702+
return PyLong_FromLong((int)NODEV);
10703+
}
10704+
return PyLong_FromUnsignedLong(result);
1067110705
}
1067210706

1067310707

1067410708
/*[clinic input]
1067510709
os.makedev -> dev_t
1067610710
10677-
major: int
10678-
minor: int
10711+
major: dev_t
10712+
minor: dev_t
1067910713
/
1068010714
1068110715
Composes a raw device number from the major and minor device numbers.
1068210716
[clinic start generated code]*/
1068310717

1068410718
static dev_t
10685-
os_makedev_impl(PyObject *module, int major, int minor)
10686-
/*[clinic end generated code: output=881aaa4aba6f6a52 input=4b9fd8fc73cbe48f]*/
10719+
os_makedev_impl(PyObject *module, dev_t major, dev_t minor)
10720+
/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/
1068710721
{
10722+
if ((major != NODEV && (dev_t)(unsigned int)major != major) ||
10723+
(minor != NODEV && (dev_t)(unsigned int)minor != minor))
10724+
{
10725+
PyErr_SetString(PyExc_OverflowError,
10726+
"Python int too large to convert to C unsigned int");
10727+
return (dev_t)-1;
10728+
}
1068810729
return makedev(major, minor);
1068910730
}
1069010731
#endif /* HAVE_DEVICE_MACROS */

0 commit comments

Comments
 (0)