| ... | ... | @@ -373,3 +373,118 @@ const Md5Writer = struct { |
| 373 | 373 | return std.fmt.bytesToHex(s, .lower); |
| 374 | 374 | } |
| 375 | 375 | }; |
| 376 | |
| 377 | test "tar should not overwrite existing file" { |
| 378 | // Starting from this folder structure: |
| 379 | // $ tree root |
| 380 | // root |
| 381 | // ├── a |
| 382 | // │   └── b |
| 383 | // │   └── c |
| 384 | // │   └── file.txt |
| 385 | // └── d |
| 386 | // └── b |
| 387 | // └── c |
| 388 | // └── file.txt |
| 389 | // |
| 390 | // Packed with command: |
| 391 | // $ cd root; tar cf overwrite_file.tar * |
| 392 | // Resulting tar has following structure: |
| 393 | // $ tar tvf overwrite_file.tar |
| 394 | // size path |
| 395 | // 0 a/ |
| 396 | // 0 a/b/ |
| 397 | // 0 a/b/c/ |
| 398 | // 2 a/b/c/file.txt |
| 399 | // 0 d/ |
| 400 | // 0 d/b/ |
| 401 | // 0 d/b/c/ |
| 402 | // 2 d/b/c/file.txt |
| 403 | // |
| 404 | // Note that there is no root folder in archive. |
| 405 | // |
| 406 | // With strip_components = 1 resulting unpacked folder was: |
| 407 | // root |
| 408 | // └── b |
| 409 | // └── c |
| 410 | // └── file.txt |
| 411 | // |
| 412 | // a/b/c/file.txt is overwritten with d/b/c/file.txt !!! |
| 413 | // This ensures that file is not overwritten. |
| 414 | // |
| 415 | const data = @embedFile("testdata/overwrite_file.tar"); |
| 416 | var fsb = std.io.fixedBufferStream(data); |
| 417 | |
| 418 | // Unpack with strip_components = 1 should fail |
| 419 | var root = std.testing.tmpDir(.{}); |
| 420 | defer root.cleanup(); |
| 421 | try testing.expectError( |
| 422 | error.PathAlreadyExists, |
| 423 | tar.pipeToFileSystem(root.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 1 }), |
| 424 | ); |
| 425 | |
| 426 | // Unpack with strip_components = 0 should pass |
| 427 | fsb.reset(); |
| 428 | var root2 = std.testing.tmpDir(.{}); |
| 429 | defer root2.cleanup(); |
| 430 | try tar.pipeToFileSystem(root2.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 0 }); |
| 431 | } |
| 432 | |
| 433 | test "tar case sensitivity" { |
| 434 | // Mimicking issue #18089, this tar contains, same file name in two case |
| 435 | // sensitive name version. Should fail on case insensitive file systems. |
| 436 | // |
| 437 | // $ tar tvf 18089.tar |
| 438 | // 18089/ |
| 439 | // 18089/alacritty/ |
| 440 | // 18089/alacritty/darkermatrix.yml |
| 441 | // 18089/alacritty/Darkermatrix.yml |
| 442 | // |
| 443 | const data = @embedFile("testdata/18089.tar"); |
| 444 | var fsb = std.io.fixedBufferStream(data); |
| 445 | |
| 446 | var root = std.testing.tmpDir(.{}); |
| 447 | defer root.cleanup(); |
| 448 | |
| 449 | tar.pipeToFileSystem(root.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { |
| 450 | // on case insensitive fs we fail on overwrite existing file |
| 451 | try testing.expectEqual(error.PathAlreadyExists, err); |
| 452 | return; |
| 453 | }; |
| 454 | |
| 455 | // on case sensitive os both files are created |
| 456 | try testing.expect((try root.dir.statFile("alacritty/darkermatrix.yml")).kind == .file); |
| 457 | try testing.expect((try root.dir.statFile("alacritty/Darkermatrix.yml")).kind == .file); |
| 458 | } |
| 459 | |
| 460 | test "tar pipeToFileSystem" { |
| 461 | // $ tar tvf |
| 462 | // pipe_to_file_system_test/ |
| 463 | // pipe_to_file_system_test/b/ |
| 464 | // pipe_to_file_system_test/b/symlink -> ../a/file |
| 465 | // pipe_to_file_system_test/a/ |
| 466 | // pipe_to_file_system_test/a/file |
| 467 | // pipe_to_file_system_test/empty/ |
| 468 | const data = @embedFile("testdata/pipe_to_file_system_test.tar"); |
| 469 | var fsb = std.io.fixedBufferStream(data); |
| 470 | |
| 471 | var root = std.testing.tmpDir(.{ .no_follow = true }); |
| 472 | defer root.cleanup(); |
| 473 | |
| 474 | tar.pipeToFileSystem(root.dir, fsb.reader(), .{ |
| 475 | .mode_mode = .ignore, |
| 476 | .strip_components = 1, |
| 477 | .exclude_empty_directories = true, |
| 478 | }) catch |err| { |
| 479 | // Skip on platform which don't support symlinks |
| 480 | if (err == error.UnableToCreateSymLink) return error.SkipZigTest; |
| 481 | return err; |
| 482 | }; |
| 483 | |
| 484 | try testing.expectError(error.FileNotFound, root.dir.statFile("empty")); |
| 485 | try testing.expect((try root.dir.statFile("a/file")).kind == .file); |
| 486 | // TODO is there better way to test symlink |
| 487 | try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink |
| 488 | var buf: [8]u8 = undefined; |
| 489 | _ = try root.dir.readLink("b/symlink", &buf); |
| 490 | } |