authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-12-11 20:18:59+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 19:37:33-07:00
log4a6d67ab1a26e0c89d55453877c1fd8b03ab1976
tree93d329ebeac6d46da461d6fef145bcb7a7773e46
parent9f7dd323082941d66c18af3da88a432835c5e3e5

tar: remove stratch from tar reader

Use explicit buffers for name, link_name instead. It is cleaner that way.

1 files changed, 74 insertions(+), 92 deletions(-)

lib/std/tar.zig+74-92
......@@ -83,11 +83,12 @@ pub const Options = struct {
8383 };
8484};
8585
86const BLOCK_SIZE = 512;
87const MAX_HEADER_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)
88
8986pub const Header = struct {
90 bytes: *const [BLOCK_SIZE]u8,
87 const SIZE = 512;
88 const MAX_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)
89 const LINK_NAME_SIZE = 100;
90
91 bytes: *const [SIZE]u8,
9192
9293 pub const FileType = enum(u8) {
9394 normal_alias = 0,
......@@ -110,7 +111,7 @@ pub const Header = struct {
110111
111112 /// Includes prefix concatenated, if any.
112113 /// TODO: check against "../" and other nefarious things
113 pub fn fullName(header: Header, buffer: *[MAX_HEADER_NAME_SIZE]u8) ![]const u8 {
114 pub fn fullName(header: Header, buffer: *[MAX_NAME_SIZE]u8) ![]const u8 {
114115 const n = name(header);
115116 const p = prefix(header);
116117 if (!is_ustar(header) or p.len == 0) {
......@@ -123,6 +124,16 @@ pub const Header = struct {
123124 return buffer[0 .. p.len + 1 + n.len];
124125 }
125126
127 pub fn linkName(header: Header, buffer: *[LINK_NAME_SIZE]u8) []const u8 {
128 const link_name = header.str(157, 100);
129 if (link_name.len == 0) {
130 return buffer[0..0];
131 }
132 const buf = buffer[0..link_name.len];
133 @memcpy(buf, link_name);
134 return buf;
135 }
136
126137 pub fn name(header: Header) []const u8 {
127138 return header.str(0, 100);
128139 }
......@@ -139,10 +150,6 @@ pub const Header = struct {
139150 return header.octal(148, 8);
140151 }
141152
142 pub fn linkName(header: Header) []const u8 {
143 return header.str(157, 100);
144 }
145
146153 pub fn is_ustar(header: Header) bool {
147154 const magic = header.bytes[257..][0..6];
148155 return std.mem.eql(u8, magic[0..5], "ustar") and (magic[5] == 0 or magic[5] == ' ');
......@@ -219,12 +226,6 @@ fn nullStr(str: []const u8) []const u8 {
219226 return str;
220227}
221228
222// Number of padding bytes in the last file block.
223inline fn blockPadding(size: usize) usize {
224 const block_rounded = std.mem.alignForward(usize, size, BLOCK_SIZE); // size rounded to te block boundary
225 return block_rounded - size;
226}
227
228229pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
229230 switch (options.mode_mode) {
230231 .ignore => {},
......@@ -936,56 +937,18 @@ pub fn tarReader(reader: anytype, diagnostics: ?*Options.Diagnostics) TarReader(
936937
937938fn TarReader(comptime ReaderType: type) type {
938939 return struct {
939 // scratch buffer for file attributes
940 scratch: struct {
941 // size: two paths (name and link_name) and files size bytes (24 in pax attribute)
942 buffer: [std.fs.MAX_PATH_BYTES * 2 + 24]u8 = undefined,
943 tail: usize = 0,
944
945 name: []const u8 = undefined,
946 link_name: []const u8 = undefined,
947 size: usize = 0,
948
949 // Allocate size of the buffer for some attribute.
950 fn alloc(self: *@This(), size: usize) ![]u8 {
951 const free_size = self.buffer.len - self.tail;
952 if (size > free_size) return error.TarScratchBufferOverflow;
953 const head = self.tail;
954 self.tail += size;
955 assert(self.tail <= self.buffer.len);
956 return self.buffer[head..self.tail];
957 }
958
959 // Reset buffer and all fields.
960 fn reset(self: *@This()) void {
961 self.tail = 0;
962 self.name = self.buffer[0..0];
963 self.link_name = self.buffer[0..0];
964 self.size = 0;
965 }
966
967 fn append(self: *@This(), header: Header) !void {
968 if (self.size == 0) self.size = try header.fileSize();
969 if (self.link_name.len == 0) {
970 const link_name = header.linkName();
971 if (link_name.len > 0) {
972 const buf = try self.alloc(link_name.len);
973 @memcpy(buf, link_name);
974 self.link_name = buf;
975 }
976 }
977 if (self.name.len == 0) {
978 self.name = try header.fullName((try self.alloc(MAX_HEADER_NAME_SIZE))[0..MAX_HEADER_NAME_SIZE]);
979 }
980 }
981 } = .{},
982
983940 reader: ReaderType,
984941 diagnostics: ?*Options.Diagnostics,
985 padding: usize = 0, // bytes of padding to the end of the block
986 header_buffer: [BLOCK_SIZE]u8 = undefined,
987942
988 const Self = @This();
943 // buffers for heeader and file attributes
944 header_buffer: [Header.SIZE]u8 = undefined,
945 file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined,
946 link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined,
947
948 // bytes of padding to the end of the block
949 padding: usize = 0,
950 // current tar file
951 file: File = undefined,
989952
990953 pub const File = struct {
991954 name: []const u8, // name of file, symlink or directory
......@@ -994,14 +957,18 @@ fn TarReader(comptime ReaderType: type) type {
994957 mode: u32,
995958 file_type: Header.FileType,
996959
997 reader: *ReaderType,
960 reader: ReaderType,
998961
999962 // Writes file content to writer.
1000963 pub fn write(self: File, writer: anytype) !void {
1001 var n = self.size;
1002 while (n > 0) : (n -= 1) {
1003 const byte: u8 = try self.reader.readByte();
1004 try writer.writeByte(byte);
964 var buffer: [4096]u8 = undefined;
965
966 var n: usize = 0;
967 while (n < self.size) {
968 const buf = buffer[0..@min(buffer.len, self.size - n)];
969 try self.reader.readNoEof(buf);
970 try writer.writeAll(buf);
971 n += buf.len;
1005972 }
1006973 }
1007974
......@@ -1011,34 +978,44 @@ fn TarReader(comptime ReaderType: type) type {
1011978 }
1012979 };
1013980
981 const Self = @This();
982
1014983 fn readHeader(self: *Self) !?Header {
1015984 if (self.padding > 0) {
1016985 try self.reader.skipBytes(self.padding, .{});
1017986 }
1018987 const n = try self.reader.readAll(&self.header_buffer);
1019988 if (n == 0) return null;
1020 if (n < BLOCK_SIZE) return error.UnexpectedEndOfStream;
1021 const header = Header{ .bytes = self.header_buffer[0..BLOCK_SIZE] };
989 if (n < Header.SIZE) return error.UnexpectedEndOfStream;
990 const header = Header{ .bytes = self.header_buffer[0..Header.SIZE] };
1022991 if (try header.checkChksum() == 0) return null;
1023992 return header;
1024993 }
1025994
1026 fn readString(self: *Self, size: usize) ![]const u8 {
1027 const buf = try self.scratch.alloc(size);
995 inline fn readString(self: *Self, size: usize, buffer: []u8) ![]const u8 {
996 assert(buffer.len >= size);
997 const buf = buffer[0..size];
1028998 try self.reader.readNoEof(buf);
1029999 return nullStr(buf);
10301000 }
10311001
1032 fn reset(self: *Self) void {
1002 inline fn initFile(self: *Self) void {
10331003 self.file = File{
10341004 .name = self.file_name_buffer[0..0],
10351005 .link_name = self.link_name_buffer[0..0],
10361006 .size = 0,
1037 .file_type = 0xff,
1007 .file_type = .normal,
10381008 .mode = 0,
1009 .reader = self.reader,
10391010 };
10401011 }
10411012
1013 // Number of padding bytes in the last file block.
1014 inline fn blockPadding(size: usize) usize {
1015 const block_rounded = std.mem.alignForward(usize, size, Header.SIZE); // size rounded to te block boundary
1016 return block_rounded - size;
1017 }
1018
10421019 // Externally, `next` iterates through the tar archive as if it is a
10431020 // series of files. Internally, the tar format often uses fake "files"
10441021 // to add meta data that describes the next file. These meta data
......@@ -1046,7 +1023,7 @@ fn TarReader(comptime ReaderType: type) type {
10461023 // loop iterates through one or more "header files" until it finds a
10471024 // "normal file".
10481025 pub fn next(self: *Self) !?File {
1049 self.scratch.reset();
1026 self.initFile();
10501027
10511028 while (try self.readHeader()) |header| {
10521029 const file_type = header.fileType();
......@@ -1056,41 +1033,46 @@ fn TarReader(comptime ReaderType: type) type {
10561033 switch (file_type) {
10571034 // File types to retrun upstream
10581035 .directory, .normal, .symbolic_link => {
1059 try self.scratch.append(header);
1060 const file = File{
1061 .file_type = file_type,
1062 .name = self.scratch.name,
1063 .link_name = self.scratch.link_name,
1064 .size = self.scratch.size,
1065 .reader = &self.reader,
1066 .mode = try header.mode(),
1067 };
1068 self.padding = blockPadding(file.size);
1069 return file;
1036 self.file.file_type = file_type;
1037 self.file.mode = try header.mode();
1038
1039 // set file attributes if not already set by prefix/extended headers
1040 if (self.file.size == 0) {
1041 self.file.size = size;
1042 }
1043 if (self.file.link_name.len == 0) {
1044 self.file.link_name = header.linkName(self.link_name_buffer[0..Header.LINK_NAME_SIZE]);
1045 }
1046 if (self.file.name.len == 0) {
1047 self.file.name = try header.fullName(self.file_name_buffer[0..Header.MAX_NAME_SIZE]);
1048 }
1049
1050 self.padding = blockPadding(self.file.size);
1051 return self.file;
10701052 },
10711053 // Prefix header types
10721054 .gnu_long_name => {
1073 self.scratch.name = try self.readString(size);
1055 self.file.name = try self.readString(size, &self.file_name_buffer);
10741056 },
10751057 .gnu_long_link => {
1076 self.scratch.link_name = try self.readString(size);
1058 self.file.link_name = try self.readString(size, &self.link_name_buffer);
10771059 },
10781060 .extended_header => {
1079 if (size == 0) continue;
10801061 // Use just attributes from last extended header.
1081 self.scratch.reset();
1062 self.initFile();
10821063
10831064 var rdr = paxReader(self.reader, size);
10841065 while (try rdr.next()) |attr| {
10851066 switch (attr.kind) {
10861067 .path => {
1087 self.scratch.name = try attr.value(try self.scratch.alloc(attr.len));
1068 self.file.name = try attr.value(&self.file_name_buffer);
10881069 },
10891070 .linkpath => {
1090 self.scratch.link_name = try attr.value(try self.scratch.alloc(attr.len));
1071 self.file.link_name = try attr.value(&self.link_name_buffer);
10911072 },
10921073 .size => {
1093 self.scratch.size = try std.fmt.parseInt(usize, try attr.value(try self.scratch.alloc(attr.len)), 10);
1074 var buf: [64]u8 = undefined;
1075 self.file.size = try std.fmt.parseInt(usize, try attr.value(&buf), 10);
10941076 },
10951077 }
10961078 }