From 440a77061fe7f94fc27435c2ff02b82d4f24014f Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 16 Oct 2023 10:53:21 +0200 Subject: [PATCH] Keep track of file permissions With this patch the file mode of files in relocatable folders are recorded when the file is originally read. When the underlying folder is removed and the contents are written to scratch space, the original file mode is set on the new files. --- src/RelocatableFolders.jl | 24 +++++++++++++++--------- test/path/readonly.txt | 1 + test/runtests.jl | 18 ++++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 test/path/readonly.txt diff --git a/src/RelocatableFolders.jl b/src/RelocatableFolders.jl index 3a0ce57..ef4d630 100644 --- a/src/RelocatableFolders.jl +++ b/src/RelocatableFolders.jl @@ -42,12 +42,18 @@ macro path(expr, ignore=:nothing) return :($(Path)($__module__, $dir, $(esc(expr)), $(esc(ignore)))) end +struct File + relative_path::String + bytes::Vector{UInt8} + mode::UInt64 +end + struct Path <: AbstractString is_dir::Bool mod::Module path::String hash::String - files::Dict{String,Vector{UInt8}} + files::Dict{String,File} function Path(mod::Module, dir, path::AbstractString, ignore=nothing) path = isabspath(path) ? path : joinpath(dir, path) @@ -55,7 +61,7 @@ struct Path <: AbstractString safe_ispath(path) || throw(ArgumentError("not a path: `$path`")) is_dir = isdir(path) dir = is_dir ? path : dirname(path) - files = Dict{String,Vector{UInt8}}() + files = Dict{String,File}() ctx = SHA.SHA1_CTX() for (root, _, fs) in walkdir(dir), f in fs fullpath = joinpath(root, f) @@ -66,11 +72,11 @@ struct Path <: AbstractString SHA.update!(ctx, codeunits(fullpath)) content = read(fullpath) SHA.update!(ctx, content) - files[rel] = content + files[rel] = File(rel, content, filemode(fullpath)) end end end - return new(is_dir, mod, dir, string(Base.SHA1(SHA.digest!(ctx))), files) + return new(is_dir, mod, dir, bytes2hex(SHA.digest!(ctx)), files) end end @@ -100,11 +106,11 @@ function getpath(f::Path) end dir = Scratch.get_scratch!(f.mod, f.hash * "_" * string(hash(f.path), base=62)) if !isempty(f.files) && !safe_ispath(joinpath(dir, first(keys(f.files)))) - cd(dir) do - for (file, blob) in f.files - mkpath(dirname(file)) - write(file, blob) - end + for file in values(f.files) + fullpath = joinpath(dir, file.relative_path) + mkpath(dirname(fullpath)) + write(fullpath, file.bytes) + chmod(fullpath, file.mode) end end return getroot(f, dir) diff --git a/test/path/readonly.txt b/test/path/readonly.txt new file mode 100644 index 0000000..a1c5e8f --- /dev/null +++ b/test/path/readonly.txt @@ -0,0 +1 @@ +This file is readonly. diff --git a/test/runtests.jl b/test/runtests.jl index 9f09ad3..090573e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -29,25 +29,31 @@ end @test read(M.OTHER, String) == "# other.jl" @test isdir(M.DIR) - @test sort(readdir(M.DIR)) == ["file.jl", "file.json", "subfolder", "text.md"] + @test sort(readdir(M.DIR)) == ["file.jl", "file.json", "readonly.txt", "subfolder", "text.md"] @test readdir(joinpath(M.DIR, "subfolder")) == ["other.jl"] @test read(joinpath(M.DIR, "file.jl"), String) == "# file.jl" @test read(joinpath(M.DIR, "text.md"), String) == "text.md" @test read(joinpath(M.DIR, "subfolder", "other.jl"), String) == "# other.jl" + @test read(joinpath(M.DIR, "readonly.txt"), String) == "This file is readonly.\n" - @test length(M.DIR.files) == 4 + # File permission test + m = filemode(joinpath(M.DIR, "readonly.txt")) + @test m & 0o400 == 0o400 # user read bit + @test m & 0o200 == 0o000 # user write bit + + @test length(M.DIR.files) == 5 @test M.DIR.mod == M @test M.DIR.path == joinpath(@__DIR__, "path") - @test length(M.IGNORE_RE.files) == 3 + @test length(M.IGNORE_RE.files) == 4 @test !haskey(M.IGNORE_RE.files, joinpath("path", "text.md")) - @test length(M.IGNORE_RE_REL.files) == 3 + @test length(M.IGNORE_RE_REL.files) == 4 @test !haskey(M.IGNORE_RE_REL.files, joinpath("path", "text.md")) @test !haskey(M.IGNORE_RE_REL.files, joinpath("path", "file.jl")) - @test length(M.IGNORE_RES.files) == 3 + @test length(M.IGNORE_RES.files) == 4 @test !haskey(M.IGNORE_RES.files, joinpath("path", "text.md")) - @test length(M.IGNORE_FN.files) == 3 + @test length(M.IGNORE_FN.files) == 4 @test !haskey(M.IGNORE_FN.files, joinpath("path", "text.md")) @test length(M.IGNORE_FN_REL.files) == 2 @test !haskey(M.IGNORE_FN_REL.files, joinpath("path", "file.jl"))