@@ -105,6 +105,8 @@ pub const Header = struct {
105
105
// used to store the path or link name for the next file.
106
106
gnu_long_name = 'L' ,
107
107
gnu_long_link = 'K' ,
108
+ gnu_sparse = 'S' ,
109
+ solaris_extended_header = 'X' ,
108
110
_ ,
109
111
};
110
112
@@ -194,25 +196,31 @@ pub const Header = struct {
194
196
return std .fmt .parseInt (u64 , rtrimmed , 8 ) catch return error .TarHeader ;
195
197
}
196
198
199
+ const Chksums = struct {
200
+ unsigned : u64 ,
201
+ signed : i64 ,
202
+ };
203
+
197
204
// Sum of all bytes in the header block. The chksum field is treated as if
198
205
// 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 ));
204
212
}
205
- // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.
206
- return if (sum > 0 ) sum + 256 else 0 ;
213
+ return cs ;
207
214
}
208
215
209
216
// Checks calculated chksum with value of chksum field.
210
217
// Returns error or valid chksum value.
211
218
// Zero value indicates empty block.
212
219
pub fn checkChksum (header : Header ) ! u64 {
213
220
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 ;
216
224
return field ;
217
225
}
218
226
};
@@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type {
387
395
.file_name = try d .allocator .dupe (u8 , header .name ()),
388
396
.file_type = kind ,
389
397
} });
398
+ if (kind == .gnu_sparse ) {
399
+ try self .skipGnuSparseExtendedHeaders (header );
400
+ }
401
+ self .reader .skipBytes (size , .{}) catch return error .TarHeadersTooBig ;
390
402
},
391
403
}
392
404
}
393
405
return null ;
394
406
}
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
+ }
395
417
};
396
418
}
397
419
0 commit comments