| ... | @@ -209,17 +209,17 @@ inline fn blockPadding(size: usize) usize { | ... | @@ -209,17 +209,17 @@ inline fn blockPadding(size: usize) usize { |
| 209 | | 209 | |
| 210 | fn BufferedReader(comptime ReaderType: type) type { | 210 | fn BufferedReader(comptime ReaderType: type) type { |
| 211 | return struct { | 211 | return struct { |
| 212 | unbuffered_reader: ReaderType, | 212 | underlying_reader: ReaderType, |
| 213 | buffer: [BLOCK_SIZE * 8]u8 = undefined, | 213 | buffer: [BLOCK_SIZE * 8]u8 = undefined, |
| 214 | start: usize = 0, | 214 | start: usize = 0, |
| 215 | end: usize = 0, | 215 | end: usize = 0, |
| 216 | | 216 | |
| 217 | const Self = @This(); | 217 | const Self = @This(); |
| 218 | | 218 | |
| 219 | // Fills buffer from underlaying reader. | 219 | // Fills buffer from underlying unbuffered reader. |
| 220 | fn fillBuffer(self: *Self) !void { | 220 | fn fillBuffer(self: *Self) !void { |
| 221 | self.removeUsed(); | 221 | 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..]); |
| 223 | } | 223 | } |
| 224 | | 224 | |
| 225 | // Returns slice of size count or how much fits into buffer. | 225 | // Returns slice of size count or how much fits into buffer. |
| ... | @@ -261,7 +261,7 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -261,7 +261,7 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 261 | // Advances reader without assuming that count bytes are in the buffer. | 261 | // Advances reader without assuming that count bytes are in the buffer. |
| 262 | pub fn skip(self: *Self, count: usize) !void { | 262 | pub fn skip(self: *Self, count: usize) !void { |
| 263 | if (self.start + count > self.end) { | 263 | 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, .{}); |
| 265 | self.start = self.end; | 265 | self.start = self.end; |
| 266 | } else { | 266 | } else { |
| 267 | self.advance(count); | 267 | self.advance(count); |
| ... | @@ -313,14 +313,14 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -313,14 +313,14 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 313 | offset: usize = 0, | 313 | offset: usize = 0, |
| 314 | reader: *Self, | 314 | reader: *Self, |
| 315 | | 315 | |
| 316 | const PaxKey = enum { | 316 | const PaxKeyKind = enum { |
| 317 | path, | 317 | path, |
| 318 | linkpath, | 318 | linkpath, |
| 319 | size, | 319 | size, |
| 320 | }; | 320 | }; |
| 321 | | 321 | |
| 322 | const PaxAttribute = struct { | 322 | const PaxAttribute = struct { |
| 323 | key: PaxKey, | 323 | key: PaxKeyKind, |
| 324 | value_len: usize, | 324 | value_len: usize, |
| 325 | parent: *PaxFileReader, | 325 | parent: *PaxFileReader, |
| 326 | | 326 | |
| ... | @@ -347,7 +347,7 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -347,7 +347,7 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 347 | try self.reader.readSlice(remaining_size), | 347 | try self.reader.readSlice(remaining_size), |
| 348 | remaining_size, | 348 | remaining_size, |
| 349 | ); | 349 | ); |
| 350 | const key: PaxKey = if (inf.is("path")) | 350 | const key: PaxKeyKind = if (inf.is("path")) |
| 351 | .path | 351 | .path |
| 352 | else if (inf.is("linkpath")) | 352 | else if (inf.is("linkpath")) |
| 353 | .linkpath | 353 | .linkpath |
| ... | @@ -376,8 +376,7 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -376,8 +376,7 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 376 | }; | 376 | }; |
| 377 | } | 377 | } |
| 378 | | 378 | |
| 379 | fn Iterator(comptime ReaderType: type) type { | 379 | fn Iterator(comptime BufferedReaderType: type) type { |
| 380 | const BufferedReaderType = BufferedReader(ReaderType); | | |
| 381 | return struct { | 380 | return struct { |
| 382 | // scratch buffer for file attributes | 381 | // scratch buffer for file attributes |
| 383 | scratch: struct { | 382 | scratch: struct { |
| ... | @@ -527,14 +526,19 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -527,14 +526,19 @@ fn Iterator(comptime ReaderType: type) type { |
| 527 | }; | 526 | }; |
| 528 | } | 527 | } |
| 529 | | 528 | |
| 530 | pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { | 529 | pub fn iterator(underlying_reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(BufferedReader(@TypeOf(underlying_reader))) { |
| 531 | const ReaderType = @TypeOf(reader); | | |
| 532 | return .{ | 530 | return .{ |
| 533 | .reader = BufferedReader(ReaderType){ .unbuffered_reader = reader }, | 531 | .reader = bufferedReader(underlying_reader), |
| 534 | .diagnostics = diagnostics, | 532 | .diagnostics = diagnostics, |
| 535 | }; | 533 | }; |
| 536 | } | 534 | } |
| 537 | | 535 | |
| | 536 | fn bufferedReader(underlying_reader: anytype) BufferedReader(@TypeOf(underlying_reader)) { |
| | 537 | return BufferedReader(@TypeOf(underlying_reader)){ |
| | 538 | .underlying_reader = underlying_reader, |
| | 539 | }; |
| | 540 | } |
| | 541 | |
| 538 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { | 542 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { |
| 539 | switch (options.mode_mode) { | 543 | switch (options.mode_mode) { |
| 540 | .ignore => {}, | 544 | .ignore => {}, |
| ... | @@ -656,7 +660,7 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { | ... | @@ -656,7 +660,7 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { |
| 656 | const pos_space = std.mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidPaxAttribute; | 660 | const pos_space = std.mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidPaxAttribute; |
| 657 | const pos_equals = std.mem.indexOfScalarPos(u8, data, pos_space, '=') orelse return error.InvalidPaxAttribute; | 661 | const pos_equals = std.mem.indexOfScalarPos(u8, data, pos_space, '=') orelse return error.InvalidPaxAttribute; |
| 658 | const kv_size = try std.fmt.parseInt(usize, data[0..pos_space], 10); | 662 | 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) { |
| 660 | return error.InvalidPaxAttribute; | 664 | return error.InvalidPaxAttribute; |
| 661 | } | 665 | } |
| 662 | const key = data[pos_space + 1 .. pos_equals]; | 666 | const key = data[pos_space + 1 .. pos_equals]; |
| ... | @@ -1057,3 +1061,94 @@ const Md5Writer = struct { | ... | @@ -1057,3 +1061,94 @@ const Md5Writer = struct { |
| 1057 | return std.fmt.bytesToHex(s, .lower); | 1061 | return std.fmt.bytesToHex(s, .lower); |
| 1058 | } | 1062 | } |
| 1059 | }; | 1063 | }; |
| | 1064 | |
| | 1065 | test "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 | } |