Commit b84301c8e5
Changed files (3)
lib
std
tar
testdata
lib/std/tar/testdata/overwrite_file.tar
Binary file
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 });
+}
+
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;
};
}