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