Skip to content

Commit a423e6a

Browse files
Tar.create: include entries for non-empty directories
Issue #103 points out that omitting directory entries for non-empty directories can confuse some tools that consume tarballs, including docker, which applies overly restrictive permissions to directories which are not explicitly included in a tarball. This commit changes `Tar.create` and `Tar.rewrite` to produce tarballs which include explicit directory entries for all (non-root) directories. This changes Tar.jl's "canonical format", which is, by design one-to-one with git tree hashes. However, it does not seem like anyone currently depends on that exact reproducibility and it seems worth making this change in order to avoid confusing external consumers of our tarballs. Closes #103.
1 parent 9316af8 commit a423e6a

File tree

3 files changed

+12
-9
lines changed

3 files changed

+12
-9
lines changed

src/create.jl

+10-8
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,26 @@ function write_tarball(
8080
tar_path::String = ".";
8181
buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
8282
)
83-
w = 0
8483
hdr, data = callback(sys_path, tar_path)
8584
if hdr.type == :directory
8685
data isa Union{Nothing, AbstractDict{<:AbstractString}} ||
8786
error("callback must return a dict of strings, got: $(repr(data))")
88-
data !== nothing && for name in sort!(collect(keys(data)))
89-
sys_path′ = data[name]
90-
tar_path′ = tar_path == "." ? name : "$tar_path/$name"
91-
w += write_tarball(callback, tar, sys_path′, tar_path′, buf=buf)
92-
end
9387
else
9488
data isa Union{Nothing, AbstractString, IO, Tuple{IO,Integer}} ||
9589
error("callback must return nothing, string or IO, got: $(repr(data))")
9690
end
97-
if hdr.type != :directory || w == 0
91+
w = 0
92+
if tar_path != "."
93+
w += write_tarball(tar, hdr, data, buf=buf)
94+
end
95+
data isa AbstractDict && for name in sort!(collect(keys(data)))
96+
sys_path′ = data[name]
97+
tar_path′ = tar_path == "." ? name : "$tar_path/$name"
98+
w += write_tarball(callback, tar, sys_path′, tar_path′, buf=buf)
99+
end
100+
if tar_path == "." && w == 0
98101
w += write_tarball(tar, hdr, data, buf=buf)
99102
end
100-
@assert w > 0
101103
return w
102104
end
103105

test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ end
212212
# check rewrite
213213
tarball′ = Tar.rewrite(tarball)
214214
@test Tar.list(tarball′) == [
215+
Tar.Header("dir", :directory, 0o755, 0, "")
215216
Tar.Header("dir/file", :file, 0o755, 0, "")
216217
Tar.Header("file", :file, 0o755, 0, "")
217218
]

test/setup.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function make_test_dir(gen_skip::Bool=false)
135135
return dir
136136
end
137137

138-
const test_dir_paths = ["dir/file", "empty", "file", "link"]
138+
const test_dir_paths = ["dir", "dir/file", "empty", "file", "link"]
139139
Sys.iswindows() && pop!(test_dir_paths)
140140

141141
# uses Tar.list(callback, tarball) API

0 commit comments

Comments
 (0)