authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-12-05 17:08:45+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 19:37:33-07:00
loga3cf8ec71ec17f384608a6df0d41b804f2cfe231
treeced02a3d0ecba47004411ab5f37673aad70fe570
parent7b0bbc680fa831200653fb0af7cb46a768e0dd93

tar: add pax file reader tests


1 files changed, 108 insertions(+), 13 deletions(-)

lib/std/tar.zig+108-13
......@@ -209,17 +209,17 @@ inline fn blockPadding(size: usize) usize {
209209
210210fn BufferedReader(comptime ReaderType: type) type {
211211 return struct {
212 unbuffered_reader: ReaderType,
212 underlying_reader: ReaderType,
213213 buffer: [BLOCK_SIZE * 8]u8 = undefined,
214214 start: usize = 0,
215215 end: usize = 0,
216216
217217 const Self = @This();
218218
219 // Fills buffer from underlaying reader.
219 // Fills buffer from underlying unbuffered reader.
220220 fn fillBuffer(self: *Self) !void {
221221 self.removeUsed();
222 self.end += try self.unbuffered_reader.read(self.buffer[self.end..]);
222 self.end += try self.underlying_reader.read(self.buffer[self.end..]);
223223 }
224224
225225 // Returns slice of size count or how much fits into buffer.
......@@ -261,7 +261,7 @@ fn BufferedReader(comptime ReaderType: type) type {
261261 // Advances reader without assuming that count bytes are in the buffer.
262262 pub fn skip(self: *Self, count: usize) !void {
263263 if (self.start + count > self.end) {
264 try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{});
264 try self.underlying_reader.skipBytes(self.start + count - self.end, .{});
265265 self.start = self.end;
266266 } else {
267267 self.advance(count);
......@@ -313,14 +313,14 @@ fn BufferedReader(comptime ReaderType: type) type {
313313 offset: usize = 0,
314314 reader: *Self,
315315
316 const PaxKey = enum {
316 const PaxKeyKind = enum {
317317 path,
318318 linkpath,
319319 size,
320320 };
321321
322322 const PaxAttribute = struct {
323 key: PaxKey,
323 key: PaxKeyKind,
324324 value_len: usize,
325325 parent: *PaxFileReader,
326326
......@@ -347,7 +347,7 @@ fn BufferedReader(comptime ReaderType: type) type {
347347 try self.reader.readSlice(remaining_size),
348348 remaining_size,
349349 );
350 const key: PaxKey = if (inf.is("path"))
350 const key: PaxKeyKind = if (inf.is("path"))
351351 .path
352352 else if (inf.is("linkpath"))
353353 .linkpath
......@@ -376,8 +376,7 @@ fn BufferedReader(comptime ReaderType: type) type {
376376 };
377377}
378378
379fn Iterator(comptime ReaderType: type) type {
380 const BufferedReaderType = BufferedReader(ReaderType);
379fn Iterator(comptime BufferedReaderType: type) type {
381380 return struct {
382381 // scratch buffer for file attributes
383382 scratch: struct {
......@@ -527,14 +526,19 @@ fn Iterator(comptime ReaderType: type) type {
527526 };
528527}
529528
530pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) {
531 const ReaderType = @TypeOf(reader);
529pub fn iterator(underlying_reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(BufferedReader(@TypeOf(underlying_reader))) {
532530 return .{
533 .reader = BufferedReader(ReaderType){ .unbuffered_reader = reader },
531 .reader = bufferedReader(underlying_reader),
534532 .diagnostics = diagnostics,
535533 };
536534}
537535
536fn bufferedReader(underlying_reader: anytype) BufferedReader(@TypeOf(underlying_reader)) {
537 return BufferedReader(@TypeOf(underlying_reader)){
538 .underlying_reader = underlying_reader,
539 };
540}
541
538542pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
539543 switch (options.mode_mode) {
540544 .ignore => {},
......@@ -656,7 +660,7 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo {
656660 const pos_space = std.mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidPaxAttribute;
657661 const pos_equals = std.mem.indexOfScalarPos(u8, data, pos_space, '=') orelse return error.InvalidPaxAttribute;
658662 const kv_size = try std.fmt.parseInt(usize, data[0..pos_space], 10);
659 if (kv_size > max_size) {
663 if (kv_size > max_size or kv_size < pos_equals + 2) {
660664 return error.InvalidPaxAttribute;
661665 }
662666 const key = data[pos_space + 1 .. pos_equals];
......@@ -1057,3 +1061,94 @@ const Md5Writer = struct {
10571061 return std.fmt.bytesToHex(s, .lower);
10581062 }
10591063};
1064
1065test "tar PaxFileReader" {
1066 const Attribute = struct {
1067 const PaxKeyKind = enum {
1068 path,
1069 linkpath,
1070 size,
1071 };
1072 key: PaxKeyKind,
1073 value: []const u8,
1074 };
1075 const cases = [_]struct {
1076 data: []const u8,
1077 attrs: []const Attribute,
1078 err: ?anyerror = null,
1079 }{
1080 .{ // valid but unknown keys
1081 .data =
1082 \\30 mtime=1350244992.023960108
1083 \\6 k=1
1084 \\13 key1=val1
1085 \\10 a=name
1086 \\9 a=name
1087 \\
1088 ,
1089 .attrs = &[_]Attribute{},
1090 },
1091 .{ // mix of known and unknown keys
1092 .data =
1093 \\6 k=1
1094 \\13 path=name
1095 \\17 linkpath=link
1096 \\13 key1=val1
1097 \\12 size=123
1098 \\13 key2=val2
1099 \\
1100 ,
1101 .attrs = &[_]Attribute{
1102 .{ .key = .path, .value = "name" },
1103 .{ .key = .linkpath, .value = "link" },
1104 .{ .key = .size, .value = "123" },
1105 },
1106 },
1107 .{ // too short size of the second key-value pair
1108 .data =
1109 \\13 path=name
1110 \\10 linkpath=value
1111 \\
1112 ,
1113 .attrs = &[_]Attribute{
1114 .{ .key = .path, .value = "name" },
1115 },
1116 .err = error.InvalidPaxAttribute,
1117 },
1118 .{ // too long size of the second key-value pair
1119 .data =
1120 \\13 path=name
1121 \\19 linkpath=value
1122 \\
1123 ,
1124 .attrs = &[_]Attribute{
1125 .{ .key = .path, .value = "name" },
1126 },
1127 .err = error.InvalidPaxAttribute,
1128 },
1129 };
1130 var buffer: [1024]u8 = undefined;
1131
1132 for (cases) |case| {
1133 var stream = std.io.fixedBufferStream(case.data);
1134 var brdr = bufferedReader(stream.reader());
1135
1136 var rdr = brdr.paxFileReader(case.data.len);
1137 var i: usize = 0;
1138 while (rdr.next() catch |err| {
1139 if (case.err) |e| {
1140 try std.testing.expectEqual(e, err);
1141 continue;
1142 } else {
1143 return err;
1144 }
1145 }) |attr| : (i += 1) {
1146 try std.testing.expectEqualStrings(
1147 case.attrs[i].value,
1148 try attr.value(&buffer),
1149 );
1150 }
1151 try std.testing.expectEqual(case.attrs.len, i);
1152 try std.testing.expect(case.err == null);
1153 }
1154}