From b84301c8e5362be1692c7812bf86e64c2ee5a850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Anic=CC=81?= Date: Sat, 24 Feb 2024 23:37:55 +0100 Subject: [PATCH] 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 --- lib/std/tar.zig | 4 +- lib/std/tar/test.zig | 57 ++++++++++++++++++++++++ lib/std/tar/testdata/overwrite_file.tar | Bin 0 -> 10240 bytes 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 lib/std/tar/testdata/overwrite_file.tar 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 0000000000000000000000000000000000000000..d01e6fdd36b581815a3022b393d4632856ad1b77 GIT binary patch literal 10240 zcmeH}O$x$5425UyDS85%`9Du>6$;`)D|mbprBF9&7a0e9*$gF2Gkhe`)p2Z+Ec z_2+JY{w4ZG^zW5BV3q#P%l*&fp?^93594vaoo_& z`NaB@&T(G<`tS39^NIss|3zR>p8Y~oZur;lf3gq%7vTSjG~!U3|9k2m68v8={j2hS zDL?$5h5iNgugL$2PZa;h^B?|CzXb;PKYhrk00JNY0w4eaAOHd&00JNY0w4eaAOHd& M00JNY0zV*d2L}<9YXATM literal 0 HcmV?d00001