diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 6c67600731..166adf9908 100644 --- 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 index f77b8a778a..a1d6acfc4b 100644 --- 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 new file mode 100644 index 0000000000..d01e6fdd36 Binary files /dev/null and b/lib/std/tar/testdata/overwrite_file.tar differ