authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-11-29 17:17:20+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 19:37:33-07:00
log16c40fc4713c195c7a6b8544c9dffbfc6201dc9d
tree2e17e3e675065d54f1e900c8bb3b714094ba2ab5
parent169f28d3e6a908717a0e42323ba1a0ee765976da

tar: add header chksum checking


1 files changed, 64 insertions(+), 38 deletions(-)

lib/std/tar.zig+64-38
...@@ -85,31 +85,6 @@ pub const Header = struct {...@@ -85,31 +85,6 @@ pub const Header = struct {
85 _,85 _,
86 };86 };
8787
88 pub fn fileSize(header: Header) !u64 {
89 const raw = header.bytes[124..][0..12];
90 // If the leading byte is 0xff (255), all the bytes of the field
91 // (including the leading byte) are concatenated in big-endian order,
92 // with the result being a negative number expressed in two’s
93 // complement form.
94 if (raw[0] == 0xff) return error.SizeNegative;
95 // If the leading byte is 0x80 (128), the non-leading bytes of the
96 // field are concatenated in big-endian order.
97 if (raw[0] == 0x80) {
98 if (raw[1] + raw[2] + raw[3] != 0) return error.SizeTooBig;
99 return std.mem.readInt(u64, raw[4..12], .big);
100 }
101 // Zero-filled octal number in ASCII. Each numeric field of width w
102 // contains w minus 1 digits, and a null
103 const ltrimmed = std.mem.trimLeft(u8, raw, "0 ");
104 const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00");
105 if (rtrimmed.len == 0) return 0;
106 return std.fmt.parseInt(u64, rtrimmed, 8);
107 }
108
109 pub fn is_ustar(header: Header) bool {
110 return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
111 }
112
113 /// Includes prefix concatenated, if any.88 /// Includes prefix concatenated, if any.
114 /// Return value may point into Header buffer, or might point into the89 /// Return value may point into Header buffer, or might point into the
115 /// argument buffer.90 /// argument buffer.
...@@ -128,15 +103,27 @@ pub const Header = struct {...@@ -128,15 +103,27 @@ pub const Header = struct {
128 }103 }
129104
130 pub fn name(header: Header) []const u8 {105 pub fn name(header: Header) []const u8 {
131 return str(header, 0, 0 + 100);106 return header.str(0, 100);
107 }
108
109 pub fn fileSize(header: Header) !u64 {
110 return header.numeric(124, 12);
111 }
112
113 pub fn chksum(header: Header) !u64 {
114 return header.octal(148, 8);
132 }115 }
133116
134 pub fn linkName(header: Header) []const u8 {117 pub fn linkName(header: Header) []const u8 {
135 return str(header, 157, 157 + 100);118 return header.str(157, 100);
119 }
120
121 pub fn is_ustar(header: Header) bool {
122 return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
136 }123 }
137124
138 pub fn prefix(header: Header) []const u8 {125 pub fn prefix(header: Header) []const u8 {
139 return str(header, 345, 345 + 155);126 return header.str(345, 155);
140 }127 }
141128
142 pub fn fileType(header: Header) FileType {129 pub fn fileType(header: Header) FileType {
...@@ -145,7 +132,8 @@ pub const Header = struct {...@@ -145,7 +132,8 @@ pub const Header = struct {
145 return result;132 return result;
146 }133 }
147134
148 fn str(header: Header, start: usize, end: usize) []const u8 {135 fn str(header: Header, start: usize, len: usize) []const u8 {
136 const end = start + len;
149 var i: usize = start;137 var i: usize = start;
150 while (i < end) : (i += 1) {138 while (i < end) : (i += 1) {
151 if (header.bytes[i] == 0) break;139 if (header.bytes[i] == 0) break;
...@@ -153,11 +141,52 @@ pub const Header = struct {...@@ -153,11 +141,52 @@ pub const Header = struct {
153 return header.bytes[start..i];141 return header.bytes[start..i];
154 }142 }
155143
156 pub fn isZero(header: Header) bool {144 fn numeric(header: Header, start: usize, len: usize) !u64 {
157 for (header.bytes) |b| {145 const raw = header.bytes[start..][0..len];
158 if (b != 0) return false;146 // If the leading byte is 0xff (255), all the bytes of the field
147 // (including the leading byte) are concatenated in big-endian order,
148 // with the result being a negative number expressed in two’s
149 // complement form.
150 if (raw[0] == 0xff) return error.TarNumericValueNegative;
151 // If the leading byte is 0x80 (128), the non-leading bytes of the
152 // field are concatenated in big-endian order.
153 if (raw[0] == 0x80) {
154 if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig;
155 return std.mem.readInt(u64, raw[4..12], .big);
159 }156 }
160 return true;157 return try header.octal(start, len);
158 }
159
160 fn octal(header: Header, start: usize, len: usize) !u64 {
161 const raw = header.bytes[start..][0..len];
162 // Zero-filled octal number in ASCII. Each numeric field of width w
163 // contains w minus 1 digits, and a null
164 const ltrimmed = std.mem.trimLeft(u8, raw, "0 ");
165 const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00");
166 if (rtrimmed.len == 0) return 0;
167 return std.fmt.parseInt(u64, rtrimmed, 8);
168 }
169
170 // Sum of all bytes in the header block. The chksum field is treated as if
171 // it were filled with spaces (ASCII 32).
172 fn computeChksum(header: Header) u64 {
173 var sum: u64 = 0;
174 for (header.bytes, 0..) |b, i| {
175 if (148 <= i and i < 156) continue; // skip chksum field bytes
176 sum += b;
177 }
178 // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.
179 return if (sum > 0) sum + 256 else 0;
180 }
181
182 // Checks calculated chksum with value of chksum field.
183 // Returns error or chksum value.
184 // Zero value indicates empty block.
185 pub fn checkChksum(header: Header) !u64 {
186 const field = try header.chksum();
187 const computed = header.computeChksum();
188 if (field != computed) return error.TarHeaderChksum;
189 return field;
161 }190 }
162};191};
163192
...@@ -368,8 +397,8 @@ fn Iterator(comptime ReaderType: type) type {...@@ -368,8 +397,8 @@ fn Iterator(comptime ReaderType: type) type {
368 self.attrs.free();397 self.attrs.free();
369398
370 while (try self.reader.readBlock()) |block_bytes| {399 while (try self.reader.readBlock()) |block_bytes| {
371 const block: Header = .{ .bytes = block_bytes[0..BLOCK_SIZE] };400 const block = Header{ .bytes = block_bytes[0..BLOCK_SIZE] };
372 if (block.isZero()) return null;401 if (try block.checkChksum() == 0) return null; // zero block found
373 const file_type = block.fileType();402 const file_type = block.fileType();
374 const file_size = try block.fileSize();403 const file_size = try block.fileSize();
375404
...@@ -578,9 +607,6 @@ test parsePaxAttribute {...@@ -578,9 +607,6 @@ test parsePaxAttribute {
578 try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0));607 try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0));
579}608}
580609
581const std = @import("std");
582const assert = std.debug.assert;
583
584const TestCase = struct {610const TestCase = struct {
585 const File = struct {611 const File = struct {
586 const empty_string = &[0]u8{};612 const empty_string = &[0]u8{};