| ... | ... | @@ -573,18 +573,6 @@ fn PaxIterator(comptime ReaderType: type) type { |
| 573 | 573 | |
| 574 | 574 | /// Saves tar file content to the file systems. |
| 575 | 575 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions) !void { |
| 576 | | switch (options.mode_mode) { |
| 577 | | .ignore => {}, |
| 578 | | .executable_bit_only => { |
| 579 | | // This code does not look at the mode bits yet. To implement this feature, |
| 580 | | // the implementation must be adjusted to look at the mode, and check the |
| 581 | | // user executable bit, then call fchmod on newly created files when |
| 582 | | // the executable bit is supposed to be set. |
| 583 | | // It also needs to properly deal with ACLs on Windows. |
| 584 | | @panic("TODO: unimplemented: tar ModeMode.executable_bit_only"); |
| 585 | | }, |
| 586 | | } |
| 587 | | |
| 588 | 576 | var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 589 | 577 | var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 590 | 578 | var iter = iterator(reader, .{ |
| ... | ... | @@ -605,7 +593,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions) |
| 605 | 593 | const file_name = stripComponents(file.name, options.strip_components); |
| 606 | 594 | if (file_name.len == 0) return error.BadFileName; |
| 607 | 595 | |
| 608 | | if (createDirAndFile(dir, file_name)) |fs_file| { |
| 596 | if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| { |
| 609 | 597 | defer fs_file.close(); |
| 610 | 598 | try file.writeAll(fs_file); |
| 611 | 599 | } else |err| { |
| ... | ... | @@ -636,12 +624,12 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions) |
| 636 | 624 | } |
| 637 | 625 | } |
| 638 | 626 | |
| 639 | | fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8) !std.fs.File { |
| 640 | | const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| { |
| 627 | fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8, mode: std.fs.File.Mode) !std.fs.File { |
| 628 | const fs_file = dir.createFile(file_name, .{ .exclusive = true, .mode = mode }) catch |err| { |
| 641 | 629 | if (err == error.FileNotFound) { |
| 642 | 630 | if (std.fs.path.dirname(file_name)) |dir_name| { |
| 643 | 631 | try dir.makePath(dir_name); |
| 644 | | return try dir.createFile(file_name, .{ .exclusive = true }); |
| 632 | return try dir.createFile(file_name, .{ .exclusive = true, .mode = mode }); |
| 645 | 633 | } |
| 646 | 634 | } |
| 647 | 635 | return err; |
| ... | ... | @@ -877,9 +865,9 @@ test "create file and symlink" { |
| 877 | 865 | var root = testing.tmpDir(.{}); |
| 878 | 866 | defer root.cleanup(); |
| 879 | 867 | |
| 880 | | var file = try createDirAndFile(root.dir, "file1"); |
| 868 | var file = try createDirAndFile(root.dir, "file1", default_mode); |
| 881 | 869 | file.close(); |
| 882 | | file = try createDirAndFile(root.dir, "a/b/c/file2"); |
| 870 | file = try createDirAndFile(root.dir, "a/b/c/file2", default_mode); |
| 883 | 871 | file.close(); |
| 884 | 872 | |
| 885 | 873 | createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| { |
| ... | ... | @@ -891,7 +879,7 @@ test "create file and symlink" { |
| 891 | 879 | |
| 892 | 880 | // Danglink symlnik, file created later |
| 893 | 881 | try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); |
| 894 | | file = try createDirAndFile(root.dir, "g/h/i/file4"); |
| 882 | file = try createDirAndFile(root.dir, "g/h/i/file4", default_mode); |
| 895 | 883 | file.close(); |
| 896 | 884 | } |
| 897 | 885 | |
| ... | ... | @@ -1011,3 +999,69 @@ fn normalizePath(bytes: []u8) []u8 { |
| 1011 | 999 | std.mem.replaceScalar(u8, bytes, std.fs.path.sep, canonical_sep); |
| 1012 | 1000 | return bytes; |
| 1013 | 1001 | } |
| 1002 | |
| 1003 | const default_mode = std.fs.File.default_mode; |
| 1004 | |
| 1005 | // File system mode based on tar header mode and mode_mode options. |
| 1006 | fn fileMode(mode: u32, options: PipeOptions) std.fs.File.Mode { |
| 1007 | if (!std.fs.has_executable_bit or options.mode_mode == .ignore) |
| 1008 | return default_mode; |
| 1009 | |
| 1010 | const S = std.posix.S; |
| 1011 | |
| 1012 | // The mode from the tar file is inspected for the owner executable bit. |
| 1013 | if (mode & S.IXUSR == 0) |
| 1014 | return default_mode; |
| 1015 | |
| 1016 | // This bit is copied to the group and other executable bits. |
| 1017 | // Other bits of the mode are left as the default when creating files. |
| 1018 | return default_mode | S.IXUSR | S.IXGRP | S.IXOTH; |
| 1019 | } |
| 1020 | |
| 1021 | test fileMode { |
| 1022 | if (!std.fs.has_executable_bit) return error.SkipZigTest; |
| 1023 | try testing.expectEqual(default_mode, fileMode(0o744, PipeOptions{ .mode_mode = .ignore })); |
| 1024 | try testing.expectEqual(0o777, fileMode(0o744, PipeOptions{})); |
| 1025 | try testing.expectEqual(0o666, fileMode(0o644, PipeOptions{})); |
| 1026 | try testing.expectEqual(0o666, fileMode(0o655, PipeOptions{})); |
| 1027 | } |
| 1028 | |
| 1029 | test "executable bit" { |
| 1030 | if (!std.fs.has_executable_bit) return error.SkipZigTest; |
| 1031 | |
| 1032 | const S = std.posix.S; |
| 1033 | const data = @embedFile("tar/testdata/example.tar"); |
| 1034 | |
| 1035 | for ([_]PipeOptions.ModeMode{ .ignore, .executable_bit_only }) |opt| { |
| 1036 | var fbs = std.io.fixedBufferStream(data); |
| 1037 | const reader = fbs.reader(); |
| 1038 | |
| 1039 | var tmp = testing.tmpDir(.{ .no_follow = true }); |
| 1040 | //defer tmp.cleanup(); |
| 1041 | |
| 1042 | pipeToFileSystem(tmp.dir, reader, .{ |
| 1043 | .strip_components = 1, |
| 1044 | .exclude_empty_directories = true, |
| 1045 | .mode_mode = opt, |
| 1046 | }) catch |err| { |
| 1047 | // Skip on platform which don't support symlinks |
| 1048 | if (err == error.UnableToCreateSymLink) return error.SkipZigTest; |
| 1049 | return err; |
| 1050 | }; |
| 1051 | |
| 1052 | const fs = try tmp.dir.statFile("a/file"); |
| 1053 | try testing.expect(fs.kind == .file); |
| 1054 | |
| 1055 | if (opt == .executable_bit_only) { |
| 1056 | // Executable bit is set for user, group and others |
| 1057 | try testing.expect(fs.mode & S.IXUSR > 0); |
| 1058 | try testing.expect(fs.mode & S.IXGRP > 0); |
| 1059 | try testing.expect(fs.mode & S.IXOTH > 0); |
| 1060 | } |
| 1061 | if (opt == .ignore) { |
| 1062 | try testing.expect(fs.mode & S.IXUSR == 0); |
| 1063 | try testing.expect(fs.mode & S.IXGRP == 0); |
| 1064 | try testing.expect(fs.mode & S.IXOTH == 0); |
| 1065 | } |
| 1066 | } |
| 1067 | } |