Skip to content

Commit cd60a2c

Browse files
committed
Implement fcntl.ioctl
Fixes #376
1 parent bf2b35f commit cd60a2c

File tree

8 files changed

+243
-4
lines changed

8 files changed

+243
-4
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.java

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -60,6 +60,9 @@
6060
import com.oracle.graal.python.builtins.modules.FcntlModuleBuiltinsClinicProviders.FlockNodeClinicProviderGen;
6161
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins.FileDescriptorConversionNode;
6262
import com.oracle.graal.python.builtins.objects.PNone;
63+
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
64+
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary;
65+
import com.oracle.graal.python.lib.PyLongAsIntNode;
6366
import com.oracle.graal.python.lib.PyLongAsLongNode;
6467
import com.oracle.graal.python.nodes.ErrorMessages;
6568
import com.oracle.graal.python.nodes.PConstructAndRaiseNode;
@@ -68,10 +71,16 @@
6871
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode;
6972
import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode;
7073
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
74+
import com.oracle.graal.python.nodes.util.CannotCastException;
75+
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
76+
import com.oracle.graal.python.runtime.GilNode;
77+
import com.oracle.graal.python.runtime.IndirectCallData;
7178
import com.oracle.graal.python.runtime.PosixConstants;
7279
import com.oracle.graal.python.runtime.PosixConstants.IntConstant;
7380
import com.oracle.graal.python.runtime.PosixSupportLibrary;
7481
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
82+
import com.oracle.graal.python.runtime.exception.PException;
83+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
7584
import com.oracle.truffle.api.dsl.Bind;
7685
import com.oracle.truffle.api.dsl.Cached;
7786
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
@@ -80,6 +89,7 @@
8089
import com.oracle.truffle.api.frame.VirtualFrame;
8190
import com.oracle.truffle.api.library.CachedLibrary;
8291
import com.oracle.truffle.api.nodes.Node;
92+
import com.oracle.truffle.api.strings.TruffleString;
8393

8494
@CoreFunctions(defineModule = "fcntl")
8595
public final class FcntlModuleBuiltins extends PythonBuiltins {
@@ -176,4 +186,150 @@ protected ArgumentClinicProvider getArgumentClinic() {
176186
return FcntlModuleBuiltinsClinicProviders.LockfNodeClinicProviderGen.INSTANCE;
177187
}
178188
}
189+
190+
@Builtin(name = "ioctl", minNumOfPositionalArgs = 2, parameterNames = {"fd", "request", "arg", "mutate_flag"})
191+
@ArgumentClinic(name = "fd", conversionClass = FileDescriptorConversionNode.class)
192+
@ArgumentClinic(name = "request", conversion = ClinicConversion.Long)
193+
@ArgumentClinic(name = "mutate_flag", conversion = ClinicConversion.Boolean, defaultValue = "true")
194+
@GenerateNodeFactory
195+
abstract static class IoctlNode extends PythonClinicBuiltinNode {
196+
private static final int IOCTL_BUFSZ = 1024;
197+
198+
@Specialization
199+
Object ioctl(VirtualFrame frame, int fd, long request, Object arg, boolean mutateArg,
200+
@Bind("this") Node inliningTarget,
201+
@CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib,
202+
@CachedLibrary(limit = "3") PythonBufferAcquireLibrary acquireLib,
203+
@CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
204+
@Cached("createFor(this)") IndirectCallData indirectCallData,
205+
@Cached PyLongAsIntNode asIntNode,
206+
@Cached CastToTruffleStringNode castToString,
207+
@Cached TruffleString.SwitchEncodingNode switchEncodingNode,
208+
@Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode,
209+
@Cached GilNode gilNode,
210+
@Cached PRaiseNode.Lazy raiseNode,
211+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode,
212+
@Cached PythonObjectFactory factory,
213+
@Cached SysModuleBuiltins.AuditNode auditNode) {
214+
auditNode.audit(inliningTarget, "fcnt.ioctl", fd, request, arg);
215+
216+
int intArg = 0;
217+
if (arg != PNone.NO_VALUE) {
218+
Object buffer = null;
219+
// Buffer argument
220+
if (acquireLib.hasBuffer(arg)) {
221+
boolean writable = false;
222+
try {
223+
buffer = acquireLib.acquireWritable(arg, frame, indirectCallData);
224+
writable = true;
225+
} catch (PException e) {
226+
try {
227+
buffer = acquireLib.acquireReadonly(arg, frame, indirectCallData);
228+
} catch (PException e1) {
229+
// ignore
230+
}
231+
}
232+
if (buffer != null) {
233+
try {
234+
int len = bufferLib.getBufferLength(buffer);
235+
boolean writeBack = false;
236+
boolean releaseGil = true;
237+
byte[] ioctlArg = null;
238+
if (writable && mutateArg) {
239+
writeBack = true;
240+
if (bufferLib.hasInternalByteArray(buffer)) {
241+
byte[] internalArray = bufferLib.getInternalByteArray(buffer);
242+
if (internalArray.length > len && internalArray[len] == 0) {
243+
writeBack = false;
244+
releaseGil = false; // Could resize concurrently
245+
ioctlArg = internalArray;
246+
}
247+
}
248+
} else {
249+
if (len > IOCTL_BUFSZ) {
250+
throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG);
251+
}
252+
}
253+
if (ioctlArg == null) {
254+
ioctlArg = new byte[len + 1];
255+
bufferLib.readIntoByteArray(buffer, 0, ioctlArg, 0, len);
256+
}
257+
try {
258+
int ret = callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, releaseGil, posixLib, gilNode, constructAndRaiseNode);
259+
if (writable && mutateArg) {
260+
return ret;
261+
} else {
262+
return factory.createBytes(ioctlArg, len);
263+
}
264+
} finally {
265+
if (writeBack) {
266+
bufferLib.writeFromByteArray(buffer, 0, ioctlArg, 0, len);
267+
}
268+
}
269+
} finally {
270+
bufferLib.release(buffer, frame, indirectCallData);
271+
}
272+
}
273+
}
274+
// string arg
275+
TruffleString stringArg = null;
276+
try {
277+
stringArg = castToString.execute(inliningTarget, arg);
278+
} catch (CannotCastException e) {
279+
// ignore
280+
}
281+
if (stringArg != null) {
282+
TruffleString.Encoding utf8 = TruffleString.Encoding.UTF_8;
283+
stringArg = switchEncodingNode.execute(stringArg, utf8);
284+
int len = stringArg.byteLength(utf8);
285+
if (len > IOCTL_BUFSZ) {
286+
throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG);
287+
}
288+
byte[] ioctlArg = new byte[len + 1];
289+
copyToByteArrayNode.execute(stringArg, 0, ioctlArg, 0, len, utf8);
290+
callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, true, posixLib, gilNode, constructAndRaiseNode);
291+
return factory.createBytes(ioctlArg, len);
292+
}
293+
294+
// int arg
295+
intArg = asIntNode.execute(frame, inliningTarget, arg);
296+
// fall through
297+
}
298+
299+
// default arg or int arg
300+
try {
301+
gilNode.release(true);
302+
try {
303+
return posixLib.ioctlInt(getPosixSupport(), fd, request, intArg);
304+
} finally {
305+
gilNode.acquire();
306+
}
307+
} catch (PosixException e) {
308+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);
309+
}
310+
}
311+
312+
private int callIoctlBytes(VirtualFrame frame, Node inliningTarget, int fd, long request, byte[] ioctlArg, boolean releaseGil, PosixSupportLibrary posixLib, GilNode gilNode,
313+
PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
314+
try {
315+
if (releaseGil) {
316+
gilNode.release(true);
317+
}
318+
try {
319+
return posixLib.ioctlBytes(getPosixSupport(), fd, request, ioctlArg);
320+
} finally {
321+
if (releaseGil) {
322+
gilNode.acquire();
323+
}
324+
}
325+
} catch (PosixException e) {
326+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);
327+
}
328+
}
329+
330+
@Override
331+
protected ArgumentClinicProvider getArgumentClinic() {
332+
return FcntlModuleBuiltinsClinicProviders.IoctlNodeClinicProviderGen.INSTANCE;
333+
}
334+
}
179335
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ public abstract class ErrorMessages {
10171017
public static final TruffleString N_OBJECT_DOES_NOT_SUPPORT_THE_ASYNC_CONTEXT_MANAGER_PROTOCOL = tsLiteral("'%N' object does not support the asynchronous context manager protocol");
10181018
public static final TruffleString N_OBJECT_DOES_NOT_SUPPORT_THE_ASYNC_CONTEXT_MANAGER_PROTOCOL_AEXIT = tsLiteral(
10191019
"'%N' object does not support the asynchronous context manager protocol (missed __aexit__ method)");
1020+
public static final TruffleString IOCTL_STRING_ARG_TOO_LONG = tsLiteral("ioctl string arg too long");
10201021

10211022
// mmap
10221023
public static final TruffleString MEM_MAPPED_LENGTH_MUST_BE_POSITIVE = tsLiteral("memory mapped length must be positive");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,18 @@ private static PwdResult createPwdResult(UnixSystem unix) {
28462846
return new PwdResult(toTruffleStringUncached(unix.getUsername()), unix.getUid(), unix.getGid(), homeDir, T_BIN_SH);
28472847
}
28482848

2849+
@ExportMessage
2850+
@SuppressWarnings("unused")
2851+
public int ioctlBytes(int fd, long request, byte[] arg) {
2852+
throw new UnsupportedPosixFeatureException("ioctl");
2853+
}
2854+
2855+
@ExportMessage
2856+
@SuppressWarnings("unused")
2857+
public int ioctlInt(int fd, long request, int arg) {
2858+
throw new UnsupportedPosixFeatureException("ioctl");
2859+
}
2860+
28492861
@ExportMessage
28502862
public int socket(int domain, int type, int protocol,
28512863
@Shared("eq") @Cached TruffleString.EqualNode eqNode) throws PosixException {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ImageBuildtimePosixSupport.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -850,6 +850,20 @@ public PwdResult[] getpwentries(@CachedLibrary("this.nativePosixSupport") PosixS
850850
return nativeLib.getpwentries(nativePosixSupport);
851851
}
852852

853+
@ExportMessage
854+
final int ioctlBytes(int fd, long request, byte[] arg,
855+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
856+
checkNotInImageBuildtime();
857+
return nativeLib.ioctlBytes(nativePosixSupport, fd, request, arg);
858+
}
859+
860+
@ExportMessage
861+
final int ioctlInt(int fd, long request, int arg,
862+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
863+
checkNotInImageBuildtime();
864+
return nativeLib.ioctlInt(nativePosixSupport, fd, request, arg);
865+
}
866+
853867
@ExportMessage
854868
final int socket(int domain, int type, int protocol,
855869
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -1089,6 +1089,28 @@ public PwdResult[] getpwentries(
10891089
}
10901090
}
10911091

1092+
@ExportMessage
1093+
final int ioctlBytes(int fd, long request, byte[] arg,
1094+
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
1095+
logEnter("ioctl", "%d %d %s", fd, request, arg);
1096+
try {
1097+
return logExit("ioctl", "%d", lib.ioctlBytes(delegate, fd, request, arg));
1098+
} catch (PosixException e) {
1099+
throw logException("ioctl", e);
1100+
}
1101+
}
1102+
1103+
@ExportMessage
1104+
final int ioctlInt(int fd, long request, int arg,
1105+
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
1106+
logEnter("ioctl", "%d %d %d", fd, request, arg);
1107+
try {
1108+
return logExit("ioctl", "%d", lib.ioctlInt(delegate, fd, request, arg));
1109+
} catch (PosixException e) {
1110+
throw logException("ioctl", e);
1111+
}
1112+
}
1113+
10921114
@ExportMessage
10931115
final int system(Object command,
10941116
@CachedLibrary("this.delegate") PosixSupportLibrary lib) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ private enum PosixNativeFunction {
301301
call_sem_trywait("(pointer):sint32"),
302302
call_sem_timedwait("(pointer, sint64):sint32"),
303303

304+
call_ioctl_bytes("(sint32, uint64, [sint8]):sint32"),
305+
call_ioctl_int("(sint32, uint64, sint32):sint32"),
306+
304307
crypt("([sint8], [sint8]):sint64");
305308

306309
private final String signature;
@@ -2463,6 +2466,26 @@ private static PwdResult createPwdResult(byte[] data, long[] output, TruffleStri
24632466
extractZeroTerminatedString(data, output[4], fromByteArrayNode, switchEncodingFromUtf8Node));
24642467
}
24652468

2469+
@ExportMessage
2470+
public int ioctlBytes(int fd, long request, byte[] arg,
2471+
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
2472+
int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_bytes, fd, request, wrap(arg));
2473+
if (res < 0) {
2474+
throw newPosixException(invokeNode, getErrno(invokeNode));
2475+
}
2476+
return res;
2477+
}
2478+
2479+
@ExportMessage
2480+
public int ioctlInt(int fd, long request, int arg,
2481+
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
2482+
int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_int, fd, request, arg);
2483+
if (res < 0) {
2484+
throw newPosixException(invokeNode, getErrno(invokeNode));
2485+
}
2486+
return res;
2487+
}
2488+
24662489
private static TruffleString extractZeroTerminatedString(byte[] buffer, long longOffset, TruffleString.FromByteArrayNode fromByteArrayNode,
24672490
TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException {
24682491
if (longOffset < 0 || longOffset >= buffer.length) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -816,6 +816,10 @@ public interface AddrInfoCursor {
816816
*/
817817
public abstract TruffleString crypt(Object receiver, TruffleString word, TruffleString salt) throws PosixException;
818818

819+
public abstract int ioctlBytes(Object receiver, int fd, long request, byte[] arg) throws PosixException;
820+
821+
public abstract int ioctlInt(Object receiver, int fd, long request, int arg) throws PosixException;
822+
819823
/**
820824
* Provides messages for manipulating {@link AddrInfoCursor}.
821825
*/

graalpython/python-libposix/src/posix.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,13 @@ int32_t get_getpwent_data(struct passwd *p, char *buffer, int32_t bufferSize, ui
10321032
return 0;
10331033
}
10341034

1035+
int32_t call_ioctl_bytes(int32_t fd, uint64_t request, char* buffer) {
1036+
return ioctl(fd, request, buffer);
1037+
}
1038+
1039+
int32_t call_ioctl_int(int32_t fd, uint64_t request, int32_t arg) {
1040+
return ioctl(fd, request, (int)arg);
1041+
}
10351042

10361043
int32_t get_errno() {
10371044
return errno;

0 commit comments

Comments
 (0)