@@ -105,6 +105,8 @@ pub const Header = struct {
105105 // used to store the path or link name for the next file.
106106 gnu_long_name = 'L' ,
107107 gnu_long_link = 'K' ,
108+ gnu_sparse = 'S' ,
109+ solaris_extended_header = 'X' ,
108110 _ ,
109111 };
110112
@@ -194,25 +196,31 @@ pub const Header = struct {
194196 return std .fmt .parseInt (u64 , rtrimmed , 8 ) catch return error .TarHeader ;
195197 }
196198
199+ const Chksums = struct {
200+ unsigned : u64 ,
201+ signed : i64 ,
202+ };
203+
197204 // Sum of all bytes in the header block. The chksum field is treated as if
198205 // it were filled with spaces (ASCII 32).
199- fn computeChksum (header : Header ) u64 {
200- var sum : u64 = 0 ;
201- for (header .bytes , 0.. ) | b , i | {
202- if (148 <= i and i < 156 ) continue ; // skip chksum field bytes
203- sum += b ;
206+ fn computeChksum (header : Header ) Chksums {
207+ var cs : Chksums = .{ .signed = 0 , .unsigned = 0 };
208+ for (header .bytes , 0.. ) | v , i | {
209+ const b = if (148 <= i and i < 156 ) 32 else v ; // Treating chksum bytes as spaces.
210+ cs .unsigned += b ;
211+ cs .signed += @as (i8 , @bitCast (b ));
204212 }
205- // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.
206- return if (sum > 0 ) sum + 256 else 0 ;
213+ return cs ;
207214 }
208215
209216 // Checks calculated chksum with value of chksum field.
210217 // Returns error or valid chksum value.
211218 // Zero value indicates empty block.
212219 pub fn checkChksum (header : Header ) ! u64 {
213220 const field = try header .chksum ();
214- const computed = header .computeChksum ();
215- if (field != computed ) return error .TarHeaderChksum ;
221+ const cs = header .computeChksum ();
222+ if (field == 0 and cs .unsigned == 256 ) return 0 ;
223+ if (field != cs .unsigned and field != cs .signed ) return error .TarHeaderChksum ;
216224 return field ;
217225 }
218226};
@@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type {
387395 .file_name = try d .allocator .dupe (u8 , header .name ()),
388396 .file_type = kind ,
389397 } });
398+ if (kind == .gnu_sparse ) {
399+ try self .skipGnuSparseExtendedHeaders (header );
400+ }
401+ self .reader .skipBytes (size , .{}) catch return error .TarHeadersTooBig ;
390402 },
391403 }
392404 }
393405 return null ;
394406 }
407+
408+ fn skipGnuSparseExtendedHeaders (self : * Self , header : Header ) ! void {
409+ var is_extended = header .bytes [482 ] > 0 ;
410+ while (is_extended ) {
411+ var buf : [Header .SIZE ]u8 = undefined ;
412+ const n = try self .reader .readAll (& buf );
413+ if (n < Header .SIZE ) return error .UnexpectedEndOfStream ;
414+ is_extended = buf [504 ] > 0 ;
415+ }
416+ }
395417 };
396418}
397419
0 commit comments