Skip to content

Commit

Permalink
Tar.extract: add set_permissions keyword argument (#113)
Browse files Browse the repository at this point in the history
Co-authored-by: Stefan Karpinski <stefan@karpinski.org>
  • Loading branch information
davidanthoff and StefanKarpinski authored Jun 8, 2021
1 parent eb7e133 commit e02d5be
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 18 deletions.
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,19 @@ recreated. The `skeleton` and `predicate` arguments cannot be used together.
### Tar.extract

```jl
extract([ predicate, ] tarball, [ dir ];
[ skeleton, ] [ copy_symlinks ]) -> dir
extract(
[ predicate, ] tarball, [ dir ];
[ skeleton = <none>, ]
[ copy_symlinks = <auto>, ]
[ set_permissions = true, ]
) -> dir
```
* `predicate :: Header --> Bool`
* `tarball :: Union{AbstractString, AbstractCmd, IO}`
* `dir :: AbstractString`
* `skeleton :: Union{AbstractString, AbstractCmd, IO}`
* `copy_symlinks :: Bool`
* `predicate :: Header --> Bool`
* `tarball :: Union{AbstractString, AbstractCmd, IO}`
* `dir :: AbstractString`
* `skeleton :: Union{AbstractString, AbstractCmd, IO}`
* `copy_symlinks :: Bool`
* `set_permissions :: Bool`

Extract a tar archive ("tarball") located at the path `tarball` into the
directory `dir`. If `tarball` is an IO object instead of a path, then the
Expand Down Expand Up @@ -105,6 +110,8 @@ will also not be copied and will instead be skipped. By default, `extract` will
detect whether symlinks can be created in `dir` or not and will automatically
copy symlinks if they cannot be created.

If `set_permissions` is `false`, no permissions are set on the extracted files.

### Tar.list

```jl
Expand Down
27 changes: 19 additions & 8 deletions src/Tar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,19 @@ function list(
end

"""
extract([ predicate, ] tarball, [ dir ];
[ skeleton, ] [ copy_symlinks ]) -> dir
predicate :: Header --> Bool
tarball :: Union{AbstractString, AbstractCmd, IO}
dir :: AbstractString
skeleton :: Union{AbstractString, AbstractCmd, IO}
copy_symlinks :: Bool
extract(
[ predicate, ] tarball, [ dir ];
[ skeleton = <none>, ]
[ copy_symlinks = <auto>, ]
[ set_permissions = true, ]
) -> dir
predicate :: Header --> Bool
tarball :: Union{AbstractString, AbstractCmd, IO}
dir :: AbstractString
skeleton :: Union{AbstractString, AbstractCmd, IO}
copy_symlinks :: Bool
set_permissions :: Bool
Extract a tar archive ("tarball") located at the path `tarball` into the
directory `dir`. If `tarball` is an IO object instead of a path, then the
Expand Down Expand Up @@ -207,13 +212,16 @@ link to `/etc/passwd` will not be copied. Symlinks which are in any way cyclic
will also not be copied and will instead be skipped. By default, `extract` will
detect whether symlinks can be created in `dir` or not and will automatically
copy symlinks if they cannot be created.
If `set_permissions` is `false`, no permissions are set on the extracted files.
"""
function extract(
predicate::Function,
tarball::ArgRead,
dir::Union{AbstractString, Nothing} = nothing;
skeleton::Union{ArgWrite, Nothing} = nothing,
copy_symlinks::Union{Bool, Nothing} = nothing,
set_permissions::Bool = true,
)
predicate === true_predicate || skeleton === nothing ||
error("extract: predicate and skeleton cannot be used together")
Expand All @@ -230,6 +238,7 @@ function extract(
predicate, tar, dir,
skeleton = skeleton,
copy_symlinks = copy_symlinks,
set_permissions = set_permissions,
)
end
end
Expand All @@ -241,11 +250,13 @@ function extract(
dir::Union{AbstractString, Nothing} = nothing;
skeleton::Union{ArgWrite, Nothing} = nothing,
copy_symlinks::Union{Bool, Nothing} = nothing,
set_permissions::Bool = true,
)
extract(
true_predicate, tarball, dir,
skeleton = skeleton,
copy_symlinks = copy_symlinks,
set_permissions = set_permissions,
)
end

Expand Down
5 changes: 3 additions & 2 deletions src/extract.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ function extract_tarball(
buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
skeleton::IO = devnull,
copy_symlinks::Bool = false,
set_permissions::Bool = true,
)
paths = read_tarball(predicate, tar; buf=buf, skeleton=skeleton) do hdr, parts
# get the file system version of the path
Expand Down Expand Up @@ -82,7 +83,7 @@ function extract_tarball(
error("unsupported tarball entry type: $(hdr.type)")
end
# apply tarball permissions
if hdr.type in (:file, :hardlink)
if set_permissions && hdr.type in (:file, :hardlink)
exec = 0o100 & hdr.mode != 0
tar_mode = exec ? 0o755 : 0o644
sys_mode = filemode(sys_path)
Expand Down Expand Up @@ -139,7 +140,7 @@ function extract_tarball(
src = reduce(joinpath, init=root, split(what, '/'))
dst = reduce(joinpath, init=root, split(path, '/'))
cp(src, dst)
if Sys.iswindows()
if set_permissions && Sys.iswindows()
# our `cp` doesn't copy ACL properties, so manually set them via `chmod`
function copy_mode(src::String, dst::String)
chmod(dst, filemode(src))
Expand Down
21 changes: 20 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ end
@arg_test tar @test_throws ErrorException tar_count(tar, strict=true)
@arg_test tar @test n + 1 == tar_count(tar, strict=false)
end
rm(tarball)
end

@testset "API: extract" begin
Expand Down Expand Up @@ -708,6 +709,23 @@ end
end
end
end
rm(tarball)

@testset "set_permissions" begin
tarball, _ = make_test_tarball()
dir = Tar.extract(tarball, set_permissions=false)
f_path = joinpath(dir, "0-ffffffff")
x_path = joinpath(dir, "0-xxxxxxxx")
@test isfile(f_path)
@test isfile(x_path)
if !Sys.iswindows()
@test !Sys.isexecutable(f_path)
@test !Sys.isexecutable(x_path)
end
@test Sys.isexecutable(f_path) == Sys.isexecutable(x_path)
rm(dir, recursive=true)
rm(tarball)
end
end

@testset "API: rewrite" begin
Expand Down Expand Up @@ -849,7 +867,7 @@ end

if Sys.iswindows() && Sys.which("icacls") !== nothing && VERSION >= v"1.6"
@testset "windows permissions" begin
tarball, hash = make_test_tarball()
tarball, _ = make_test_tarball()
mktempdir() do dir
Tar.extract(tarball, dir)
f_path = joinpath(dir, "0-ffffffff")
Expand All @@ -865,6 +883,7 @@ if Sys.iswindows() && Sys.which("icacls") !== nothing && VERSION >= v"1.6"
x_acl = readchomp(`icacls $(x_path)`)
@test occursin("Everyone:(RX,WA)", x_acl)
end
rm(tarball)
end
end

Expand Down

0 comments on commit e02d5be

Please sign in to comment.