Skip to content

Commit f5a6779

Browse files
committed
Update Sys.isexecutable() to use new jl_fs_access() API call
`jl_fs_access()` makes use of the `uv_fs_access()` call from LibUV, which is newly-able to interrogate Win32 ACLs, determining whether a file is truly executable or not on Windows. Also update `chmod()` to note that on windows, recursive is effectively always true.
1 parent a82bc01 commit f5a6779

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

base/file.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,11 @@ Change the permissions mode of `path` to `mode`. Only integer `mode`s (e.g. `0o7
989989
currently supported. If `recursive=true` and the path is a directory all permissions in
990990
that directory will be recursively changed.
991991
Return `path`.
992+
993+
!!! note
994+
Prior to Julia 1.5, this did not correctly manipulate filesystem ACLs
995+
on Windows, therefore it would only set read-only bits on files. It
996+
now is able to manipulate ACLs.
992997
"""
993998
function chmod(path::AbstractString, mode::Integer; recursive::Bool=false)
994999
err = ccall(:jl_fs_chmod, Int32, (Cstring, Cint), path, mode)

base/sysinfo.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -438,16 +438,18 @@ const WINDOWS_VISTA_VER = v"6.0"
438438
Sys.isexecutable(path::String)
439439
440440
Return `true` if the given `path` has executable permissions.
441+
442+
!!! note
443+
Prior to Julia 1.5, this did not correctly interrogate filesystem
444+
ACLs on Windows, therefore it would return `true` for any
445+
file. From Julia 1.5 on, it correctly determines whether the
446+
file is marked as executable or not.
441447
"""
442448
function isexecutable(path::String)
443-
if iswindows()
444-
return isfile(path)
445-
else
446-
# We use `access()` and `X_OK` to determine if a given path is
447-
# executable by the current user. `X_OK` comes from `unistd.h`.
448-
X_OK = 0x01
449-
ccall(:access, Cint, (Ptr{UInt8}, Cint), path, X_OK) == 0
450-
end
449+
# We use `access()` and `X_OK` to determine if a given path is
450+
# executable by the current user. `X_OK` comes from `unistd.h`.
451+
X_OK = 0x01
452+
return ccall(:jl_fs_access, Cint, (Ptr{UInt8}, Cint), path, X_OK) == 0
451453
end
452454
isexecutable(path::AbstractString) = isexecutable(String(path))
453455

src/jl_uv.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,14 @@ JL_DLLEXPORT int jl_fs_chown(char *path, int uid, int gid)
392392
return ret;
393393
}
394394

395+
JL_DLLEXPORT int jl_fs_access(char *path, int mode)
396+
{
397+
uv_fs_t req;
398+
int ret = uv_fs_access(unused_uv_loop_arg, &req, path, mode, NULL);
399+
uv_fs_req_cleanup(&req);
400+
return ret;
401+
}
402+
395403
JL_DLLEXPORT int jl_fs_write(uv_os_fd_t handle, const char *data, size_t len,
396404
int64_t offset) JL_NOTSAFEPOINT
397405
{

test/file.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,3 +1510,27 @@ end
15101510
end
15111511
end
15121512
end
1513+
1514+
@testset "chmod/isexecutable" begin
1515+
mktempdir() do dir
1516+
mkdir(joinpath(dir, "subdir"))
1517+
fpath = joinpath(dir, "subdir", "foo")
1518+
1519+
# Test that we can actually set the executable bit on all platforms.
1520+
touch(fpath)
1521+
chmod(fpath, 0o644)
1522+
@test !Sys.isexecutable(fpath)
1523+
chmod(fpath, 0o755)
1524+
@test Sys.isexecutable(fpath)
1525+
1526+
# Ensure that, on Windows, where inheritance is default,
1527+
# chmod still behaves as we expect.
1528+
if Sys.iswindows()
1529+
chmod(joinpath(dir, "subdir"), 0o666)
1530+
@test Sys.isexecutable(fpath)
1531+
end
1532+
1533+
# Reset permissions to all at the end, so it can be deleted properly.
1534+
chmod(dir, 0o777; recursive=true)
1535+
end
1536+
end

0 commit comments

Comments
 (0)