Skip to content

Commit 68588b7

Browse files
committed
Implement os.chown family of functions
1 parent be997b7 commit 68588b7

9 files changed

Lines changed: 260 additions & 22 deletions

File tree

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java‎

Lines changed: 176 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
110110
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
111111
import com.oracle.graal.python.nodes.util.CastToJavaLongLossyNode;
112-
import com.oracle.graal.python.nodes.util.CastToJavaUnsignedLongNode;
113112
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
114113
import com.oracle.graal.python.runtime.GilNode;
115114
import com.oracle.graal.python.runtime.PosixConstants;
@@ -2212,6 +2211,134 @@ PNone chmodFollow(VirtualFrame frame, PosixFd fd, int mode, int dirFd, @Suppress
22122211
}
22132212
}
22142213

2214+
@Builtin(name = "fchown", parameterNames = {"fd", "uid", "gid"})
2215+
@ArgumentClinic(name = "fd", conversion = ClinicConversion.Int)
2216+
@ArgumentClinic(name = "uid", conversionClass = UidConversionNode.class)
2217+
@ArgumentClinic(name = "gid", conversionClass = GidConversionNode.class)
2218+
@GenerateNodeFactory
2219+
abstract static class FChownNode extends PythonTernaryClinicBuiltinNode {
2220+
@Specialization
2221+
Object chown(VirtualFrame frame, int fd, long uid, long gid,
2222+
@Bind("this") Node inliningTarget,
2223+
@Cached SysModuleBuiltins.AuditNode auditNode,
2224+
@CachedLibrary(limit = "1") PosixSupportLibrary posixLib,
2225+
@Cached GilNode gil,
2226+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
2227+
auditNode.audit(inliningTarget, "os.chown", fd, uid, gid, -1);
2228+
try {
2229+
gil.release(true);
2230+
try {
2231+
posixLib.fchown(getPosixSupport(), fd, uid, gid);
2232+
} finally {
2233+
gil.acquire();
2234+
}
2235+
} catch (PosixException e) {
2236+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, fd);
2237+
}
2238+
return PNone.NONE;
2239+
}
2240+
2241+
@Override
2242+
protected ArgumentClinicProvider getArgumentClinic() {
2243+
return PosixModuleBuiltinsClinicProviders.FChownNodeClinicProviderGen.INSTANCE;
2244+
}
2245+
}
2246+
2247+
@Builtin(name = "lchown", parameterNames = {"path", "uid", "gid"})
2248+
@ArgumentClinic(name = "path", conversionClass = PathConversionNode.class, args = {"false", "false"})
2249+
@ArgumentClinic(name = "uid", conversionClass = UidConversionNode.class)
2250+
@ArgumentClinic(name = "gid", conversionClass = GidConversionNode.class)
2251+
@GenerateNodeFactory
2252+
abstract static class LChownNode extends PythonTernaryClinicBuiltinNode {
2253+
@Specialization
2254+
Object chown(VirtualFrame frame, PosixPath path, long uid, long gid,
2255+
@Bind("this") Node inliningTarget,
2256+
@Cached SysModuleBuiltins.AuditNode auditNode,
2257+
@CachedLibrary(limit = "1") PosixSupportLibrary posixLib,
2258+
@Cached GilNode gil,
2259+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
2260+
auditNode.audit(inliningTarget, "os.chown", path.originalObject, uid, gid, -1);
2261+
try {
2262+
gil.release(true);
2263+
try {
2264+
posixLib.fchownat(getPosixSupport(), AT_FDCWD.value, path.value, uid, gid, false);
2265+
} finally {
2266+
gil.acquire();
2267+
}
2268+
} catch (PosixException e) {
2269+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, path.originalObject);
2270+
}
2271+
return PNone.NONE;
2272+
}
2273+
2274+
@Override
2275+
protected ArgumentClinicProvider getArgumentClinic() {
2276+
return PosixModuleBuiltinsClinicProviders.LChownNodeClinicProviderGen.INSTANCE;
2277+
}
2278+
}
2279+
2280+
@Builtin(name = "chown", minNumOfPositionalArgs = 3, parameterNames = {"path", "uid", "gid"}, keywordOnlyNames = {"dir_fd", "follow_symlinks"})
2281+
@ArgumentClinic(name = "path", conversionClass = PathConversionNode.class, args = {"false", "true"})
2282+
@ArgumentClinic(name = "uid", conversionClass = UidConversionNode.class)
2283+
@ArgumentClinic(name = "gid", conversionClass = GidConversionNode.class)
2284+
@ArgumentClinic(name = "dir_fd", conversionClass = DirFdConversionNode.class)
2285+
@ArgumentClinic(name = "follow_symlinks", conversion = ClinicConversion.Boolean, defaultValue = "true")
2286+
@GenerateNodeFactory
2287+
abstract static class ChownNode extends PythonClinicBuiltinNode {
2288+
@Specialization
2289+
Object chown(VirtualFrame frame, PosixPath path, long uid, long gid, int dirFd, boolean followSymlinks,
2290+
@Bind("this") Node inliningTarget,
2291+
@Shared @Cached SysModuleBuiltins.AuditNode auditNode,
2292+
@Shared @CachedLibrary(limit = "1") PosixSupportLibrary posixLib,
2293+
@Shared @Cached GilNode gil,
2294+
@Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
2295+
auditNode.audit(inliningTarget, "os.chown", path.originalObject, uid, gid, dirFd != AT_FDCWD.value ? dirFd : -1);
2296+
try {
2297+
gil.release(true);
2298+
try {
2299+
posixLib.fchownat(getPosixSupport(), dirFd, path.value, uid, gid, followSymlinks);
2300+
} finally {
2301+
gil.acquire();
2302+
}
2303+
} catch (PosixException e) {
2304+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, path.originalObject);
2305+
}
2306+
return PNone.NONE;
2307+
}
2308+
2309+
@Specialization
2310+
Object chown(VirtualFrame frame, PosixFd fd, long uid, long gid, int dirFd, boolean followSymlinks,
2311+
@Bind("this") Node inliningTarget,
2312+
@Shared @Cached SysModuleBuiltins.AuditNode auditNode,
2313+
@Shared @CachedLibrary(limit = "1") PosixSupportLibrary posixLib,
2314+
@Shared @Cached GilNode gil,
2315+
@Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
2316+
if (dirFd != AT_FDCWD.value) {
2317+
throw raise(ValueError, ErrorMessages.CANT_SPECIFY_BOTH_DIR_FD_AND_FD);
2318+
}
2319+
if (followSymlinks) {
2320+
throw raise(ValueError, ErrorMessages.CANNOT_USE_FD_AND_FOLLOW_SYMLINKS_TOGETHER, "chown");
2321+
}
2322+
auditNode.audit(inliningTarget, "os.chown", fd.originalObject, uid, gid, -1);
2323+
try {
2324+
gil.release(true);
2325+
try {
2326+
posixLib.fchown(getPosixSupport(), fd.fd, uid, gid);
2327+
} finally {
2328+
gil.acquire();
2329+
}
2330+
} catch (PosixException e) {
2331+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, fd.originalObject);
2332+
}
2333+
return PNone.NONE;
2334+
}
2335+
2336+
@Override
2337+
protected ArgumentClinicProvider getArgumentClinic() {
2338+
return PosixModuleBuiltinsClinicProviders.ChownNodeClinicProviderGen.INSTANCE;
2339+
}
2340+
}
2341+
22152342
@Builtin(name = "readlink", minNumOfPositionalArgs = 1, parameterNames = {"path"}, varArgsMarker = true, keywordOnlyNames = {"dir_fd"}, doc = "readlink(path, *, dir_fd=None) -> path\n" +
22162343
"\nReturn a string representing the path to which the symbolic link points.\n")
22172344
@ArgumentClinic(name = "path", conversionClass = PathConversionNode.class, args = {"false", "false"})
@@ -3240,10 +3367,11 @@ public static PidtConversionNode create() {
32403367
}
32413368
}
32423369

3243-
/**
3244-
* Emulates CPython's {@code _Py_Uid_Converter()}. Always returns an {@code long}.
3245-
*/
3246-
public abstract static class UidConversionNode extends ArgumentCastNodeWithRaise {
3370+
@GenerateCached(false)
3371+
public abstract static class AbstractIdConversionNode extends ArgumentCastNodeWithRaise {
3372+
3373+
private static final long MAX_UINT32 = (1L << 32) - 1;
3374+
32473375
public abstract long executeLong(VirtualFrame frame, Object value);
32483376

32493377
@Specialization
@@ -3261,39 +3389,69 @@ long doLong(long value) {
32613389
long doGeneric(VirtualFrame frame, Object value,
32623390
@Bind("this") Node inliningTarget,
32633391
@Cached PyNumberIndexNode pyNumberIndexNode,
3264-
@Cached PyLongAsLongAndOverflowNode asLongAndOverflowNode,
3265-
@Cached CastToJavaUnsignedLongNode asUnsignedLong) {
3392+
@Cached PyLongAsLongNode asLongNode) {
32663393
Object index;
32673394
try {
32683395
index = pyNumberIndexNode.execute(frame, inliningTarget, value);
32693396
} catch (PException ex) {
3270-
throw raise(TypeError, ErrorMessages.UID_SHOULD_BE_INTEGER_NOT_P, value);
3271-
}
3272-
try {
3273-
return checkValue(asLongAndOverflowNode.execute(frame, inliningTarget, index));
3274-
} catch (OverflowException e) {
3275-
// fall through
3397+
throw raise(TypeError, ErrorMessages.S_SHOULD_BE_INTEGER_NOT_P, getIdName(), value);
32763398
}
3277-
return asUnsignedLong.execute(inliningTarget, index);
3278-
// We have no means to distinguish overflow/underflow so we just let any OverflowError
3279-
// from asUnsignedLong fall through. It will not have the same message as CPython, but
3280-
// still correct type.
3399+
/*
3400+
* We have no means to distinguish overflow/underflow, so we just let any OverflowError
3401+
* from asLongNode fall through. It will not have the same message as CPython, but still
3402+
* correct type.
3403+
*/
3404+
return checkValue(asLongNode.execute(frame, inliningTarget, index));
32813405
}
32823406

32833407
private long checkValue(long value) {
3408+
// Note that -1 is intentionally allowed
32843409
if (value < -1) {
3285-
throw raise(OverflowError, ErrorMessages.UID_IS_LESS_THAN_MINIMUM);
3410+
throw raise(OverflowError, ErrorMessages.S_IS_LESS_THAN_MINIMUM, getIdName());
3411+
} else if (value > MAX_UINT32) {
3412+
/* uid_t is uint32_t on Linux */
3413+
throw raise(OverflowError, ErrorMessages.S_IS_GREATER_THAN_MAXIUMUM, getIdName());
32863414
}
32873415
return value;
32883416
}
32893417

3418+
protected abstract String getIdName();
3419+
}
3420+
3421+
/**
3422+
* Emulates CPython's {@code _Py_Uid_Converter()}. Always returns a {@code long}.
3423+
*/
3424+
public abstract static class UidConversionNode extends AbstractIdConversionNode {
3425+
3426+
@Override
3427+
protected String getIdName() {
3428+
return "uid";
3429+
}
3430+
32903431
@ClinicConverterFactory(shortCircuitPrimitive = {PrimitiveType.Int, PrimitiveType.Long})
32913432
@NeverDefault
32923433
public static UidConversionNode create() {
32933434
return PosixModuleBuiltinsFactory.UidConversionNodeGen.create();
32943435
}
32953436
}
32963437

3438+
/**
3439+
* Emulates CPython's {@code _Py_Gid_Converter()}. Always returns a {@code long}.
3440+
*/
3441+
public abstract static class GidConversionNode extends AbstractIdConversionNode {
3442+
3443+
@Override
3444+
protected String getIdName() {
3445+
return "gid";
3446+
}
3447+
3448+
@ClinicConverterFactory(shortCircuitPrimitive = {PrimitiveType.Int, PrimitiveType.Long})
3449+
@NeverDefault
3450+
public static GidConversionNode create() {
3451+
return PosixModuleBuiltinsFactory.GidConversionNodeGen.create();
3452+
}
3453+
}
3454+
32973455
/**
32983456
* Represents the result of {@code path_t} conversion. Similar to CPython's {@code path_t}
32993457
* structure, but only contains the results of the conversion, not the conversion parameters.

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongAsLongNode.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
/**
6060
* Equivalent of CPython's {@code PyLong_AsLong}. Converts an object into a Java long using it's
61-
* {@code __index__} or (deprecated) {@code __int__} method. Raises {@code OverflowError} on
61+
* {@code __index__} method. Raises {@code OverflowError} on
6262
* overflow.
6363
*/
6464
@GenerateUncached

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ public abstract class ErrorMessages {
198198
public static final TruffleString CANNOT_SPECIFY_BOTH_COMMA_AND_UNDERSCORE = tsLiteral("Cannot specify both ',' and '_'.");
199199
public static final TruffleString CANNOT_SPECIFY_C_WITH_C = tsLiteral("Cannot specify '%c' with '%c'.");
200200
public static final TruffleString CANNOT_USE_FD_AND_FOLLOW_SYMLINKS_TOGETHER = tsLiteral("%s: cannot use fd and follow_symlinks together");
201+
public static final TruffleString CANT_SPECIFY_BOTH_DIR_FD_AND_FD = tsLiteral("%s: can't specify both dir_fd and fd");
201202
public static final TruffleString CANNOT_CONVERT_FLOAT_INFINITY_TO_INTEGER = tsLiteral("cannot convert float infinity to integer");
202203
public static final TruffleString CANNOT_CONVERT_FLOAT_NAN_TO_INTEGER = tsLiteral("cannot convert float NaN to integer");
203204
public static final TruffleString CANT_CAPTURE_NAME_UNDERSCORE_IN_PATTERNS = tsLiteral("can't capture name '_' in patterns");
@@ -859,8 +860,9 @@ public abstract class ErrorMessages {
859860
public static final TruffleString LINE_BUFFERING_ISNT_SUPPORTED = tsLiteral("line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used");
860861
public static final TruffleString UNCLOSED_FILE = tsLiteral("unclosed file %r");
861862
public static final TruffleString MAXIMUM_RECURSION_DEPTH_EXCEEDED = tsLiteral("maximum recursion depth exceeded");
862-
public static final TruffleString UID_IS_LESS_THAN_MINIMUM = tsLiteral("uid is less than minimum");
863-
public static final TruffleString UID_SHOULD_BE_INTEGER_NOT_P = tsLiteral("uid should be integer, not %p");
863+
public static final TruffleString S_IS_LESS_THAN_MINIMUM = tsLiteral("%s is less than minimum");
864+
public static final TruffleString S_IS_GREATER_THAN_MAXIUMUM = tsLiteral("%s is greater than maxiumum");
865+
public static final TruffleString S_SHOULD_BE_INTEGER_NOT_P = tsLiteral("%s should be integer, not %p");
864866
public static final TruffleString RANGE_OBJ_IDX_OUT_OF_RANGE = tsLiteral("range object index out of range");
865867
public static final TruffleString NUMBER_OF_BITS_MUST_BE_NON_NEGATIVE = tsLiteral("number of bits must be non-negative");
866868
public static final TruffleString TIMEOUT_MUST_BE_NON_NEG_NUM = tsLiteral("'timeout' must be a non-negative number");

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,6 +1809,16 @@ public void fchmod(int fd, int mode,
18091809
}
18101810
}
18111811

1812+
@ExportMessage
1813+
public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks) {
1814+
throw new UnsupportedPosixFeatureException("Emulated fchownat not supported");
1815+
}
1816+
1817+
@ExportMessage
1818+
public void fchown(int fd, long owner, long group) {
1819+
throw new UnsupportedPosixFeatureException("Emulated fchown not supported");
1820+
}
1821+
18121822
@ExportMessage
18131823
public Object readlinkat(int dirFd, Object path,
18141824
@Bind("$node") Node inliningTarget,

‎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
@@ -45,7 +45,6 @@
4545
import java.util.HashSet;
4646
import java.util.IdentityHashMap;
4747

48-
import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException;
4948
import org.graalvm.nativeimage.ImageInfo;
5049

5150
import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult;
@@ -55,6 +54,7 @@
5554
import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr;
5655
import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr;
5756
import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException;
57+
import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException;
5858
import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult;
5959
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
6060
import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult;
@@ -537,6 +537,20 @@ final void fchmod(int fd, int mode,
537537
nativeLib.fchmod(nativePosixSupport, fd, mode);
538538
}
539539

540+
@ExportMessage
541+
final void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks,
542+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
543+
checkNotInImageBuildtime();
544+
nativeLib.fchownat(nativePosixSupport, dirFd, path, owner, group, followSymlinks);
545+
}
546+
547+
@ExportMessage
548+
final void fchown(int fd, long owner, long group,
549+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
550+
checkNotInImageBuildtime();
551+
nativeLib.fchown(nativePosixSupport, fd, owner, group);
552+
}
553+
540554
@ExportMessage
541555
final Object readlinkat(int dirFd, Object path,
542556
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,28 @@ final void fchmod(int fd, int mode,
663663
}
664664
}
665665

666+
@ExportMessage
667+
final void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks,
668+
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
669+
logEnter("fchownat", "%d, %s, %d, %d, %b", dirFd, path, owner, group, followSymlinks);
670+
try {
671+
lib.fchownat(delegate, dirFd, path, owner, group, followSymlinks);
672+
} catch (PosixException e) {
673+
throw logException("fchownat", e);
674+
}
675+
}
676+
677+
@ExportMessage
678+
final void fchown(int fd, long owner, long group,
679+
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
680+
logEnter("fchown", "%d, %d, %d", fd, owner, group);
681+
try {
682+
lib.fchown(delegate, fd, owner, group);
683+
} catch (PosixException e) {
684+
throw logException("fchown", e);
685+
}
686+
}
687+
666688
@ExportMessage
667689
final Object readlinkat(int dirFd, Object path,
668690
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ private enum PosixNativeFunction {
206206
call_faccessat("(sint32, [sint8], sint32, sint32, sint32):sint32"),
207207
call_fchmodat("(sint32, [sint8], sint32, sint32):sint32"),
208208
call_fchmod("(sint32, sint32):sint32"),
209+
call_fchownat("(sint32, [sint8], sint64, sint64, sint32):sint32"),
210+
call_fchown("(sint32, sint64, sint64):sint32"),
209211
call_readlinkat("(sint32, [sint8], [sint8], uint64):sint64"),
210212
get_inheritable("(sint32):sint32"),
211213
set_inheritable("(sint32, sint32):sint32"),
@@ -1033,6 +1035,24 @@ public void fchmod(int fd, int mode,
10331035
}
10341036
}
10351037

1038+
@ExportMessage
1039+
public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks,
1040+
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
1041+
int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchownat, dirFd, pathToCString(path), owner, group, followSymlinks ? 1 : 0);
1042+
if (ret != 0) {
1043+
throw newPosixException(invokeNode, getErrno(invokeNode));
1044+
}
1045+
}
1046+
1047+
@ExportMessage
1048+
public void fchown(int fd, long owner, long group,
1049+
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
1050+
int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchown, fd, owner, group);
1051+
if (ret != 0) {
1052+
throw newPosixException(invokeNode, getErrno(invokeNode));
1053+
}
1054+
}
1055+
10361056
@ExportMessage
10371057
public Object readlinkat(int dirFd, Object path,
10381058
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {

0 commit comments

Comments
 (0)