Skip to content

Commit 945c93f

Browse files
fattenederKristofferC
authored andcommitted
Add Sys.isreadable, Sys.iswriteable, update ispath (#53320)
As discussed here: #53286 (comment) Readds the methods that were removed in #12819. (cherry picked from commit ea2b255)
1 parent 00cbe4a commit 945c93f

File tree

4 files changed

+102
-6
lines changed

4 files changed

+102
-6
lines changed

base/stat.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,16 @@ otherwise returns `false`.
326326
This is the generalization of [`isfile`](@ref), [`isdir`](@ref) etc.
327327
"""
328328
ispath(st::StatStruct) = filemode(st) & 0xf000 != 0x0000
329+
function ispath(path::String)
330+
# We use `access()` and `F_OK` to determine if a given path exists. `F_OK` comes from `unistd.h`.
331+
F_OK = 0x00
332+
r = ccall(:jl_fs_access, Cint, (Cstring, Cint), path, F_OK)
333+
if !(r in (0, Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL))
334+
uv_error(string("ispath(", repr(path), ")"), r)
335+
end
336+
return r == 0
337+
end
338+
ispath(path::AbstractString) = ispath(String(path))
329339

330340
"""
331341
isfifo(path) -> Bool

base/sysinfo.jl

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export BINDIR,
3333
iswindows,
3434
isjsvm,
3535
isexecutable,
36+
isreadable,
37+
iswriteable,
3638
username,
3739
which
3840

@@ -556,20 +558,81 @@ const WINDOWS_VISTA_VER = v"6.0"
556558
557559
Return `true` if the given `path` has executable permissions.
558560
561+
!!! note
562+
This permission may change before the user executes `path`,
563+
so it is recommended to execute the file and handle the error if that fails,
564+
rather than calling `isexecutable` first.
565+
559566
!!! note
560567
Prior to Julia 1.6, this did not correctly interrogate filesystem
561568
ACLs on Windows, therefore it would return `true` for any
562569
file. From Julia 1.6 on, it correctly determines whether the
563570
file is marked as executable or not.
571+
572+
See also [`ispath`](@ref), [`isreadable`](@ref), [`iswriteable`](@ref).
564573
"""
565574
function isexecutable(path::String)
566575
# We use `access()` and `X_OK` to determine if a given path is
567576
# executable by the current user. `X_OK` comes from `unistd.h`.
568577
X_OK = 0x01
569-
return ccall(:jl_fs_access, Cint, (Ptr{UInt8}, Cint), path, X_OK) == 0
578+
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, X_OK) == 0
570579
end
571580
isexecutable(path::AbstractString) = isexecutable(String(path))
572581

582+
"""
583+
Sys.isreadable(path::String)
584+
585+
Return `true` if the access permissions for the given `path` permitted reading by the current user.
586+
587+
!!! note
588+
This permission may change before the user calls `open`,
589+
so it is recommended to just call `open` alone and handle the error if that fails,
590+
rather than calling `isreadable` first.
591+
592+
!!! note
593+
Currently this function does not correctly interrogate filesystem
594+
ACLs on Windows, therefore it can return wrong results.
595+
596+
!!! compat "Julia 1.11"
597+
This function requires at least Julia 1.11.
598+
599+
See also [`ispath`](@ref), [`isexecutable`](@ref), [`iswriteable`](@ref).
600+
"""
601+
function isreadable(path::String)
602+
# We use `access()` and `R_OK` to determine if a given path is
603+
# readable by the current user. `R_OK` comes from `unistd.h`.
604+
R_OK = 0x04
605+
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, R_OK) == 0
606+
end
607+
isreadable(path::AbstractString) = isreadable(String(path))
608+
609+
"""
610+
Sys.iswriteable(path::String)
611+
612+
Return `true` if the access permissions for the given `path` permitted writing by the current user.
613+
614+
!!! note
615+
This permission may change before the user calls `open`,
616+
so it is recommended to just call `open` alone and handle the error if that fails,
617+
rather than calling `iswriteable` first.
618+
619+
!!! note
620+
Currently this function does not correctly interrogate filesystem
621+
ACLs on Windows, therefore it can return wrong results.
622+
623+
!!! compat "Julia 1.11"
624+
This function requires at least Julia 1.11.
625+
626+
See also [`ispath`](@ref), [`isexecutable`](@ref), [`isreadable`](@ref).
627+
"""
628+
function iswriteable(path::String)
629+
# We use `access()` and `W_OK` to determine if a given path is
630+
# writeable by the current user. `W_OK` comes from `unistd.h`.
631+
W_OK = 0x02
632+
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, W_OK) == 0
633+
end
634+
iswriteable(path::AbstractString) = iswriteable(String(path))
635+
573636
"""
574637
Sys.which(program_name::String)
575638

doc/src/base/base.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ Base.Sys.uptime
374374
Base.Sys.isjsvm
375375
Base.Sys.loadavg
376376
Base.Sys.isexecutable
377+
Base.Sys.isreadable
378+
Base.Sys.iswriteable
377379
Base.Sys.username
378380
Base.@static
379381
```

test/file.jl

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,23 +1660,44 @@ else
16601660
)
16611661
end
16621662

1663-
@testset "chmod/isexecutable" begin
1663+
@testset "chmod/isexecutable/isreadable/iswriteable" begin
16641664
mktempdir() do dir
1665-
mkdir(joinpath(dir, "subdir"))
1665+
subdir = joinpath(dir, "subdir")
16661666
fpath = joinpath(dir, "subdir", "foo")
16671667

1668-
# Test that we can actually set the executable bit on all platforms.
1668+
@test !ispath(subdir)
1669+
mkdir(subdir)
1670+
@test ispath(subdir)
1671+
1672+
@test !ispath(fpath)
16691673
touch(fpath)
1674+
@test ispath(fpath)
1675+
1676+
# Test that we can actually set the executable/readable/writeable bit on all platforms.
16701677
chmod(fpath, 0o644)
16711678
@test !Sys.isexecutable(fpath)
1679+
@test Sys.isreadable(fpath)
1680+
Sys.iswindows() ? @test_skip(Sys.iswriteable(fpath)) : @test(Sys.iswriteable(fpath))
16721681
chmod(fpath, 0o755)
16731682
@test Sys.isexecutable(fpath)
1683+
@test Sys.isreadable(fpath)
1684+
Sys.iswindows() ? @test_skip(Sys.iswriteable(fpath)) : @test(Sys.iswriteable(fpath))
1685+
chmod(fpath, 0o444)
1686+
@test !Sys.isexecutable(fpath)
1687+
@test Sys.isreadable(fpath)
1688+
@test !Sys.iswriteable(fpath)
1689+
chmod(fpath, 0o244)
1690+
@test !Sys.isexecutable(fpath)
1691+
Sys.iswindows() ? @test_skip(!Sys.isreadable(fpath)) : @test(!Sys.isreadable(fpath))
1692+
Sys.iswindows() ? @test_skip(Sys.iswriteable(fpath)) : @test(Sys.iswriteable(fpath))
16741693

16751694
# Ensure that, on Windows, where inheritance is default,
16761695
# chmod still behaves as we expect.
16771696
if Sys.iswindows()
1678-
chmod(joinpath(dir, "subdir"), 0o666)
1679-
@test Sys.isexecutable(fpath)
1697+
chmod(subdir, 0o666)
1698+
@test !Sys.isexecutable(fpath)
1699+
@test Sys.isreadable(fpath)
1700+
@test_skip Sys.iswriteable(fpath)
16801701
end
16811702

16821703
# Reset permissions to all at the end, so it can be deleted properly.

0 commit comments

Comments
 (0)