authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 22:36:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-11 15:39:48-08:00
log585b9970ef58e17dd88f454007949a57c4f378c8
tree6ff873a89de43b3c4ec9c417bfdfb2e8a49c2a65
parent4f6981bbe3d7bed5af60a58d012e4d2225a9c838

add std.tar for tar file unpacking


2 files changed, 146 insertions(+), 0 deletions(-)

lib/std/std.zig+2
...@@ -21,6 +21,7 @@ pub const EnumMap = enums.EnumMap;...@@ -21,6 +21,7 @@ pub const EnumMap = enums.EnumMap;
21pub const EnumSet = enums.EnumSet;21pub const EnumSet = enums.EnumSet;
22pub const HashMap = hash_map.HashMap;22pub const HashMap = hash_map.HashMap;
23pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;23pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
24pub const Ini = @import("Ini.zig");
24pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;25pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
25pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;26pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
26pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;27pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
...@@ -85,6 +86,7 @@ pub const rand = @import("rand.zig");...@@ -85,6 +86,7 @@ pub const rand = @import("rand.zig");
85pub const sort = @import("sort.zig");86pub const sort = @import("sort.zig");
86pub const simd = @import("simd.zig");87pub const simd = @import("simd.zig");
87pub const ascii = @import("ascii.zig");88pub const ascii = @import("ascii.zig");
89pub const tar = @import("tar.zig");
88pub const testing = @import("testing.zig");90pub const testing = @import("testing.zig");
89pub const time = @import("time.zig");91pub const time = @import("time.zig");
90pub const tz = @import("tz.zig");92pub const tz = @import("tz.zig");
lib/std/tar.zig created+144
...@@ -0,0 +1,144 @@
1pub const Options = struct {};
2
3pub const Header = struct {
4 bytes: *const [512]u8,
5
6 pub const FileType = enum(u8) {
7 normal = '0',
8 hard_link = '1',
9 symbolic_link = '2',
10 character_special = '3',
11 block_special = '4',
12 directory = '5',
13 fifo = '6',
14 contiguous = '7',
15 global_extended_header = 'g',
16 extended_header = 'x',
17 _,
18 };
19
20 pub fn fileSize(header: Header) !u64 {
21 const raw = header.bytes[124..][0..12];
22 const ltrimmed = std.mem.trimLeft(u8, raw, "0");
23 const rtrimmed = std.mem.trimRight(u8, ltrimmed, "\x00");
24 if (rtrimmed.len == 0) return 0;
25 return std.fmt.parseInt(u64, rtrimmed, 8);
26 }
27
28 pub fn is_ustar(header: Header) bool {
29 return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
30 }
31
32 /// Includes prefix concatenated, if any.
33 /// Return value may point into Header buffer, or might point into the
34 /// argument buffer.
35 /// TODO: check against "../" and other nefarious things
36 pub fn fullFileName(header: Header, buffer: *[255]u8) ![]const u8 {
37 const n = name(header);
38 if (!is_ustar(header))
39 return n;
40 const p = prefix(header);
41 if (p.len == 0)
42 return n;
43 std.mem.copy(u8, buffer[0..p.len], p);
44 buffer[p.len] = '/';
45 std.mem.copy(u8, buffer[p.len + 1 ..], n);
46 return buffer[0 .. p.len + 1 + n.len];
47 }
48
49 pub fn name(header: Header) []const u8 {
50 return str(header, 0, 0 + 100);
51 }
52
53 pub fn prefix(header: Header) []const u8 {
54 return str(header, 345, 345 + 155);
55 }
56
57 pub fn fileType(header: Header) FileType {
58 const result = @intToEnum(FileType, header.bytes[156]);
59 return if (result == @intToEnum(FileType, 0)) .normal else result;
60 }
61
62 fn str(header: Header, start: usize, end: usize) []const u8 {
63 var i: usize = start;
64 while (i < end) : (i += 1) {
65 if (header.bytes[i] == 0) break;
66 }
67 return header.bytes[start..i];
68 }
69};
70
71pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
72 _ = options;
73 var file_name_buffer: [255]u8 = undefined;
74 var buffer: [512 * 8]u8 = undefined;
75 var start: usize = 0;
76 var end: usize = 0;
77 header: while (true) {
78 if (buffer.len - start < 1024) {
79 std.mem.copy(u8, &buffer, buffer[start..end]);
80 end -= start;
81 start = 0;
82 }
83 const ask_header = @min(buffer.len - end, 1024 -| (end - start));
84 end += try reader.readAtLeast(buffer[end..], ask_header);
85 switch (end - start) {
86 0 => return,
87 1...511 => return error.UnexpectedEndOfStream,
88 else => {},
89 }
90 const header: Header = .{ .bytes = buffer[start..][0..512] };
91 start += 512;
92 const file_size = try header.fileSize();
93 const rounded_file_size = std.mem.alignForwardGeneric(u64, file_size, 512);
94 const pad_len = rounded_file_size - file_size;
95 const file_name = try header.fullFileName(&file_name_buffer);
96 switch (header.fileType()) {
97 .directory => {
98 try dir.makeDir(file_name);
99 },
100 .normal => {
101 if (file_size == 0 and file_name.len == 0) return;
102
103 var file = try dir.createFile(file_name, .{});
104 defer file.close();
105
106 var file_off: usize = 0;
107 while (true) {
108 if (buffer.len - start < 1024) {
109 std.mem.copy(u8, &buffer, buffer[start..end]);
110 end -= start;
111 start = 0;
112 }
113 // Ask for the rounded up file size + 512 for the next header.
114 const ask = @min(
115 buffer.len - end,
116 rounded_file_size + 512 - file_off -| (end - start),
117 );
118 end += try reader.readAtLeast(buffer[end..], ask);
119 if (end - start < ask) return error.UnexpectedEndOfStream;
120 const slice = buffer[start..@min(file_size - file_off + start, end)];
121 try file.writeAll(slice);
122 file_off += slice.len;
123 start += slice.len;
124 if (file_off >= file_size) {
125 start += pad_len;
126 // Guaranteed since we use a buffer divisible by 512.
127 assert(start <= end);
128 continue :header;
129 }
130 }
131 },
132 .global_extended_header, .extended_header => {
133 start += rounded_file_size;
134 if (start > end) return error.TarHeadersTooBig;
135 },
136 .hard_link => return error.TarUnsupportedFileType,
137 .symbolic_link => return error.TarUnsupportedFileType,
138 else => return error.TarUnsupportedFileType,
139 }
140 }
141}
142
143const std = @import("std.zig");
144const assert = std.debug.assert;