zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit b84301c8e5362be1692c7812bf86e64c2ee5a850 (tree)
parent 6fddc9cd3d8002a44cf65e05d32c449bde6ca60c
Author: Igor Anić <igor.anic@gmail.com>
Date:   Sat, 24 Feb 2024 23:37:55 +0100

std.tar don't overwrite existing file

Fail with error if file already exists. File is not silently overwritten
but an error is raised.

Fixes: #18089

Diffstat:
Mlib/std/tar.zig | 4++--
Mlib/std/tar/test.zig | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/std/tar/testdata/overwrite_file.tar | 0
3 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/lib/std/tar.zig b/lib/std/tar.zig @@ -544,12 +544,12 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi const file_name = stripComponents(file.name, options.strip_components); if (file_name.len == 0) return error.BadFileName; - const fs_file = dir.createFile(file_name, .{}) catch |err| switch (err) { + const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| switch (err) { error.FileNotFound => again: { const code = code: { if (std.fs.path.dirname(file_name)) |dir_name| { dir.makePath(dir_name) catch |code| break :code code; - break :again dir.createFile(file_name, .{}) catch |code| { + break :again dir.createFile(file_name, .{ .exclusive = true }) catch |code| { break :code code; }; } diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig @@ -369,3 +369,60 @@ const Md5Writer = struct { return std.fmt.bytesToHex(s, .lower); } }; + +test "tar should not overwrite existing file" { + // Starting from this folder structure: + // $ tree root + // root + // ├── a + // │   └── b + // │   └── c + // │   └── file.txt + // └── d + // └── b + // └── c + // └── file.txt + // + // Packed with command: + // $ cd root; tar cf overwrite_file.tar * + // Resulting tar has following structure: + // $ tar tvf overwrite_file.tar + // size path + // 0 a/ + // 0 a/b/ + // 0 a/b/c/ + // 2 a/b/c/file.txt + // 0 d/ + // 0 d/b/ + // 0 d/b/c/ + // 2 d/b/c/file.txt + // + // Note that there is no root folder in archive. + // + // With strip_components = 1 resulting unpacked folder was: + // root + // └── b + // └── c + // └── file.txt + // + // a/b/c/file.txt is overwritten with d/b/c/file.txt !!! + // This ensures that file is not overwritten. + // + const data = @embedFile("testdata/overwrite_file.tar"); + var fsb = std.io.fixedBufferStream(data); + + // Unpack with strip_components = 1 should fail + var root = std.testing.tmpDir(.{}); + defer root.cleanup(); + try testing.expectError( + error.PathAlreadyExists, + tar.pipeToFileSystem(root.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 1 }), + ); + + // Unpack with strip_components = 0 should pass + fsb.reset(); + var root2 = std.testing.tmpDir(.{}); + defer root2.cleanup(); + try tar.pipeToFileSystem(root2.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 0 }); +} + diff --git a/lib/std/tar/testdata/overwrite_file.tar b/lib/std/tar/testdata/overwrite_file.tar Binary files differ.