diff --git a/README.md b/README.md index e62cba5..4629484 100644 --- a/README.md +++ b/README.md @@ -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 = , ] + [ copy_symlinks = , ] + [ 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 @@ -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 diff --git a/src/Tar.jl b/src/Tar.jl index 8452233..a580019 100644 --- a/src/Tar.jl +++ b/src/Tar.jl @@ -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 = , ] + [ copy_symlinks = , ] + [ 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 @@ -207,6 +212,8 @@ 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, @@ -214,6 +221,7 @@ function extract( 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") @@ -230,6 +238,7 @@ function extract( predicate, tar, dir, skeleton = skeleton, copy_symlinks = copy_symlinks, + set_permissions = set_permissions, ) end end @@ -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 diff --git a/src/extract.jl b/src/extract.jl index f8b3770..e7f182e 100644 --- a/src/extract.jl +++ b/src/extract.jl @@ -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 @@ -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) @@ -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)) diff --git a/test/runtests.jl b/test/runtests.jl index b76fc6e..0dbe1b0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -704,6 +705,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 @@ -845,7 +863,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") @@ -861,5 +879,6 @@ 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