mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Fix bug
[ 555817 ] Flawed fcntl.ioctl implementation. with my patch that allows for an array to be mutated when passed as the buffer argument to ioctl() (details complicated by backwards compatibility considerations -- read the docs!).
This commit is contained in:
parent
122152451e
commit
f008998668
4 changed files with 171 additions and 18 deletions
|
@ -47,10 +47,57 @@ The module defines the following functions:
|
||||||
raised.
|
raised.
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{ioctl}{fd, op, arg}
|
\begin{funcdesc}{ioctl}{fd, op\optional{, arg\optional{, mutate_flag}}}
|
||||||
This function is identical to the \function{fcntl()} function, except
|
This function is identical to the \function{fcntl()} function,
|
||||||
that the operations are typically defined in the library module
|
except that the operations are typically defined in the library
|
||||||
\refmodule{termios}.
|
module \refmodule{termios} and the argument handling is even more
|
||||||
|
complicated.
|
||||||
|
|
||||||
|
The parameter \var{arg} can be one of an integer, absent (treated
|
||||||
|
identically to the integer \code{0}), an object supporting the
|
||||||
|
read-only buffer interface (most likely a plain Python string) or an
|
||||||
|
object supporting the read-write buffer interface.
|
||||||
|
|
||||||
|
In all but the last case, behaviour is as for the \function{fcntl()}
|
||||||
|
function.
|
||||||
|
|
||||||
|
If a mutable buffer is passed, then the behaviour is determined by
|
||||||
|
the value of the \var{mutate_flag} parameter.
|
||||||
|
|
||||||
|
If it is false, the buffer's mutability is ignored and behaviour is
|
||||||
|
as for a read-only buffer, except that the 1024 byte limit mentioned
|
||||||
|
above is avoided -- so long as the buffer you pass is longer than
|
||||||
|
what the operating system wants to put there, things should work.
|
||||||
|
|
||||||
|
If \var{mutate_flag} is true, then the buffer is (in effect) passed
|
||||||
|
to the underlying \function{ioctl()} system call, the latter's
|
||||||
|
return code is passed back to the calling Python, and the buffer's
|
||||||
|
new contents reflect the action of the \function{ioctl}. This is a
|
||||||
|
slight simplification, because if the supplied buffer is less than
|
||||||
|
1024 bytes long it is first copied into a static buffer 1024 bytes
|
||||||
|
long which is then passed to \function{ioctl} and copied back into
|
||||||
|
the supplied buffer.
|
||||||
|
|
||||||
|
If \var{mutate_flag} is not supplied, then in 2.3 it defaults to
|
||||||
|
false. This is planned to change over the next few Python versions:
|
||||||
|
in 2.4 failing to supply \var{mutate_flag} will get a warning but
|
||||||
|
the same behavior and in versions later than 2.5 it will default to
|
||||||
|
true.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
>>> import array, fnctl, struct, termios, os
|
||||||
|
>>> os.getpgrp()
|
||||||
|
13341
|
||||||
|
>>> struct.unpack('h', fcntl.ioctl(0, termios.TIOCGPGRP, " "))[0]
|
||||||
|
13341
|
||||||
|
>>> buf = array.array('h', [0])
|
||||||
|
>>> fcntl.ioctl(0, termios.TIOCGPGRP, buf, 1)
|
||||||
|
0
|
||||||
|
>>> buf
|
||||||
|
array('h', [13341])
|
||||||
|
\end{verbatim}
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{flock}{fd, op}
|
\begin{funcdesc}{flock}{fd, op}
|
||||||
|
|
31
Lib/test/test_ioctl.py
Normal file
31
Lib/test/test_ioctl.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import unittest
|
||||||
|
from test_support import TestSkipped, run_unittest
|
||||||
|
import os, struct
|
||||||
|
try:
|
||||||
|
import fcntl, termios
|
||||||
|
except ImportError:
|
||||||
|
raise TestSkipped("No fcntl or termios module")
|
||||||
|
if not hasattr(termios,'TIOCGPGRP'):
|
||||||
|
raise TestSkipped("termios module doesn't have TIOCGPGRP")
|
||||||
|
|
||||||
|
class IoctlTests(unittest.TestCase):
|
||||||
|
def test_ioctl(self):
|
||||||
|
pgrp = os.getpgrp()
|
||||||
|
tty = open("/dev/tty", "r")
|
||||||
|
r = fcntl.ioctl(tty, termios.TIOCGPGRP, " ")
|
||||||
|
self.assertEquals(pgrp, struct.unpack("i", r)[0])
|
||||||
|
|
||||||
|
def test_ioctl_mutate(self):
|
||||||
|
import array
|
||||||
|
buf = array.array('i', [0])
|
||||||
|
pgrp = os.getpgrp()
|
||||||
|
tty = open("/dev/tty", "r")
|
||||||
|
r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1)
|
||||||
|
self.assertEquals(r, 0)
|
||||||
|
self.assertEquals(pgrp, buf[0])
|
||||||
|
|
||||||
|
def test_main():
|
||||||
|
run_unittest(IoctlTests)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_main()
|
|
@ -24,6 +24,9 @@ Core and builtins
|
||||||
Extension modules
|
Extension modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Modified the fcntl.ioctl() function to allow modification of a passed
|
||||||
|
mutable buffer (for details see the reference documentation).
|
||||||
|
|
||||||
- Made user requested changes to the itertools module.
|
- Made user requested changes to the itertools module.
|
||||||
Subsumed the times() function into repeat().
|
Subsumed the times() function into repeat().
|
||||||
Added chain() and cycle().
|
Added chain() and cycle().
|
||||||
|
|
|
@ -99,8 +99,62 @@ fcntl_ioctl(PyObject *self, PyObject *args)
|
||||||
int ret;
|
int ret;
|
||||||
char *str;
|
char *str;
|
||||||
int len;
|
int len;
|
||||||
|
int mutate_arg = 0;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
|
|
||||||
|
if (PyArg_ParseTuple(args, "O&iw#|i:ioctl",
|
||||||
|
conv_descriptor, &fd, &code,
|
||||||
|
&str, &len, &mutate_arg)) {
|
||||||
|
char *arg;
|
||||||
|
|
||||||
|
if (PyTuple_Size(args) == 3) {
|
||||||
|
/* warning goes here in 2.4 */
|
||||||
|
mutate_arg = 0;
|
||||||
|
}
|
||||||
|
if (mutate_arg) {
|
||||||
|
if (len <= sizeof buf) {
|
||||||
|
memcpy(buf, str, len);
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arg = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (len > sizeof buf) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"ioctl string arg too long");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(buf, str, len);
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buf == arg) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS /* think array.resize() */
|
||||||
|
ret = ioctl(fd, code, arg);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = ioctl(fd, code, arg);
|
||||||
|
}
|
||||||
|
if (mutate_arg && (len < sizeof buf)) {
|
||||||
|
memcpy(str, buf, len);
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
PyErr_SetFromErrno(PyExc_IOError);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (mutate_arg) {
|
||||||
|
return PyInt_FromLong(ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return PyString_FromStringAndSize(buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Clear();
|
||||||
if (PyArg_ParseTuple(args, "O&is#:ioctl",
|
if (PyArg_ParseTuple(args, "O&is#:ioctl",
|
||||||
conv_descriptor, &fd, &code, &str, &len)) {
|
conv_descriptor, &fd, &code, &str, &len)) {
|
||||||
if (len > sizeof buf) {
|
if (len > sizeof buf) {
|
||||||
|
@ -123,7 +177,7 @@ fcntl_ioctl(PyObject *self, PyObject *args)
|
||||||
arg = 0;
|
arg = 0;
|
||||||
if (!PyArg_ParseTuple(args,
|
if (!PyArg_ParseTuple(args,
|
||||||
"O&i|i;ioctl requires a file or file descriptor,"
|
"O&i|i;ioctl requires a file or file descriptor,"
|
||||||
" an integer and optionally a third integer or a string",
|
" an integer and optionally a integer or buffer argument",
|
||||||
conv_descriptor, &fd, &code, &arg)) {
|
conv_descriptor, &fd, &code, &arg)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -138,17 +192,35 @@ fcntl_ioctl(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(ioctl_doc,
|
PyDoc_STRVAR(ioctl_doc,
|
||||||
"ioctl(fd, opt, [arg])\n\
|
"ioctl(fd, opt[, arg[, mutate_flag]])\n\
|
||||||
\n\
|
\n\
|
||||||
Perform the requested operation on file descriptor fd. The operation\n\
|
Perform the requested operation on file descriptor fd. The operation is\n\
|
||||||
is defined by op and is operating system dependent. Typically these\n\
|
defined by op and is operating system dependent. Typically these codes are\n\
|
||||||
codes can be retrieved from the library module IOCTL. The argument arg\n\
|
retrieved from the fcntl or termios library modules.\n\
|
||||||
is optional, and defaults to 0; it may be an int or a string. If arg is\n\
|
\n\
|
||||||
given as a string, the return value of ioctl is a string of that length,\n\
|
The argument arg is optional, and defaults to 0; it may be an int or a\n\
|
||||||
containing the resulting value put in the arg buffer by the operating system.\n\
|
buffer containing character data (most likely a string or an array). \n\
|
||||||
The length of the arg string is not allowed to exceed 1024 bytes. If the arg\n\
|
\n\
|
||||||
given is an integer or if none is specified, the result value is an integer\n\
|
If the argument is a mutable buffer (such as an array) and if the\n\
|
||||||
corresponding to the return value of the ioctl call in the C code.");
|
mutate_flag argument (which is only allowed in this case) is true then the\n\
|
||||||
|
buffer is (in effect) passed to the operating system and changes made by\n\
|
||||||
|
the OS will be reflected in the contents of the buffer after the call has\n\
|
||||||
|
returned. The return value is the integer returned by the ioctl system\n\
|
||||||
|
call.\n\
|
||||||
|
\n\
|
||||||
|
If the argument is a mutable buffer and the mutable_flag argument is not\n\
|
||||||
|
passed or is false, the behavior is as if a string had been passed. This\n\
|
||||||
|
behavior will change in future releases of Python.\n\
|
||||||
|
\n\
|
||||||
|
If the argument is an immutable buffer (most likely a string) then a copy\n\
|
||||||
|
of the buffer is passed to the operating system and the return value is a\n\
|
||||||
|
string of the same length containing whatever the operating system put in\n\
|
||||||
|
the buffer. The length of the arg buffer in this case is not allowed to\n\
|
||||||
|
exceed 1024 bytes.\n\
|
||||||
|
\n\
|
||||||
|
If the arg given is an integer or if none is specified, the result value is\n\
|
||||||
|
an integer corresponding to the return value of the ioctl call in the C\n\
|
||||||
|
code.");
|
||||||
|
|
||||||
|
|
||||||
/* flock(fd, operation) */
|
/* flock(fd, operation) */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue