From a3cf8ec71ec17f384608a6df0d41b804f2cfe231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Anic=CC=81?= Date: Tue, 5 Dec 2023 17:08:45 +0100 Subject: [PATCH] tar: add pax file reader tests --- lib/std/tar.zig | 121 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 13 deletions(-) diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 51c1c023ae..6d1934d91c 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -209,17 +209,17 @@ inline fn blockPadding(size: usize) usize { fn BufferedReader(comptime ReaderType: type) type { return struct { - unbuffered_reader: ReaderType, + underlying_reader: ReaderType, buffer: [BLOCK_SIZE * 8]u8 = undefined, start: usize = 0, end: usize = 0, const Self = @This(); - // Fills buffer from underlaying reader. + // Fills buffer from underlying unbuffered reader. fn fillBuffer(self: *Self) !void { self.removeUsed(); - self.end += try self.unbuffered_reader.read(self.buffer[self.end..]); + self.end += try self.underlying_reader.read(self.buffer[self.end..]); } // Returns slice of size count or how much fits into buffer. @@ -261,7 +261,7 @@ fn BufferedReader(comptime ReaderType: type) type { // Advances reader without assuming that count bytes are in the buffer. pub fn skip(self: *Self, count: usize) !void { if (self.start + count > self.end) { - try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{}); + try self.underlying_reader.skipBytes(self.start + count - self.end, .{}); self.start = self.end; } else { self.advance(count); @@ -313,14 +313,14 @@ fn BufferedReader(comptime ReaderType: type) type { offset: usize = 0, reader: *Self, - const PaxKey = enum { + const PaxKeyKind = enum { path, linkpath, size, }; const PaxAttribute = struct { - key: PaxKey, + key: PaxKeyKind, value_len: usize, parent: *PaxFileReader, @@ -347,7 +347,7 @@ fn BufferedReader(comptime ReaderType: type) type { try self.reader.readSlice(remaining_size), remaining_size, ); - const key: PaxKey = if (inf.is("path")) + const key: PaxKeyKind = if (inf.is("path")) .path else if (inf.is("linkpath")) .linkpath @@ -376,8 +376,7 @@ fn BufferedReader(comptime ReaderType: type) type { }; } -fn Iterator(comptime ReaderType: type) type { - const BufferedReaderType = BufferedReader(ReaderType); +fn Iterator(comptime BufferedReaderType: type) type { return struct { // scratch buffer for file attributes scratch: struct { @@ -527,14 +526,19 @@ fn Iterator(comptime ReaderType: type) type { }; } -pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { - const ReaderType = @TypeOf(reader); +pub fn iterator(underlying_reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(BufferedReader(@TypeOf(underlying_reader))) { return .{ - .reader = BufferedReader(ReaderType){ .unbuffered_reader = reader }, + .reader = bufferedReader(underlying_reader), .diagnostics = diagnostics, }; } +fn bufferedReader(underlying_reader: anytype) BufferedReader(@TypeOf(underlying_reader)) { + return BufferedReader(@TypeOf(underlying_reader)){ + .underlying_reader = underlying_reader, + }; +} + pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { switch (options.mode_mode) { .ignore => {}, @@ -656,7 +660,7 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { const pos_space = std.mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidPaxAttribute; const pos_equals = std.mem.indexOfScalarPos(u8, data, pos_space, '=') orelse return error.InvalidPaxAttribute; const kv_size = try std.fmt.parseInt(usize, data[0..pos_space], 10); - if (kv_size > max_size) { + if (kv_size > max_size or kv_size < pos_equals + 2) { return error.InvalidPaxAttribute; } const key = data[pos_space + 1 .. pos_equals]; @@ -1057,3 +1061,94 @@ const Md5Writer = struct { return std.fmt.bytesToHex(s, .lower); } }; + +test "tar PaxFileReader" { + const Attribute = struct { + const PaxKeyKind = enum { + path, + linkpath, + size, + }; + key: PaxKeyKind, + value: []const u8, + }; + const cases = [_]struct { + data: []const u8, + attrs: []const Attribute, + err: ?anyerror = null, + }{ + .{ // valid but unknown keys + .data = + \\30 mtime=1350244992.023960108 + \\6 k=1 + \\13 key1=val1 + \\10 a=name + \\9 a=name + \\ + , + .attrs = &[_]Attribute{}, + }, + .{ // mix of known and unknown keys + .data = + \\6 k=1 + \\13 path=name + \\17 linkpath=link + \\13 key1=val1 + \\12 size=123 + \\13 key2=val2 + \\ + , + .attrs = &[_]Attribute{ + .{ .key = .path, .value = "name" }, + .{ .key = .linkpath, .value = "link" }, + .{ .key = .size, .value = "123" }, + }, + }, + .{ // too short size of the second key-value pair + .data = + \\13 path=name + \\10 linkpath=value + \\ + , + .attrs = &[_]Attribute{ + .{ .key = .path, .value = "name" }, + }, + .err = error.InvalidPaxAttribute, + }, + .{ // too long size of the second key-value pair + .data = + \\13 path=name + \\19 linkpath=value + \\ + , + .attrs = &[_]Attribute{ + .{ .key = .path, .value = "name" }, + }, + .err = error.InvalidPaxAttribute, + }, + }; + var buffer: [1024]u8 = undefined; + + for (cases) |case| { + var stream = std.io.fixedBufferStream(case.data); + var brdr = bufferedReader(stream.reader()); + + var rdr = brdr.paxFileReader(case.data.len); + var i: usize = 0; + while (rdr.next() catch |err| { + if (case.err) |e| { + try std.testing.expectEqual(e, err); + continue; + } else { + return err; + } + }) |attr| : (i += 1) { + try std.testing.expectEqualStrings( + case.attrs[i].value, + try attr.value(&buffer), + ); + } + try std.testing.expectEqual(case.attrs.len, i); + try std.testing.expect(case.err == null); + } +}