Skip to content

Commit cf7f620

Browse files
committed
tar: Do not pad zero-length blocks
1 parent 90e62ec commit cf7f620

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

Sources/Tar/tar.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ public func tarHeader(filesize: Int, filename: String = "app") throws -> [UInt8]
201201
return hdr
202202
}
203203

204+
let blockSize = 512
205+
206+
/// Returns the number of padding bytes to be appended to a file.
207+
/// Each file in a tar archive must be padded to a multiple of the 512 byte block size.
208+
/// - Parameter len: The length of the archive member.
209+
/// - Returns: The number of zero bytes to append as padding.
210+
func padding(_ len: Int) -> Int {
211+
(blockSize - len % blockSize) % blockSize
212+
}
213+
204214
/// Creates a tar archive containing a single file
205215
/// - Parameters:
206216
/// - bytes: The file's body data
@@ -214,7 +224,7 @@ public func tar(_ bytes: [UInt8], filename: String = "app") throws -> [UInt8] {
214224
hdr.append(contentsOf: bytes)
215225

216226
// Pad the file data to a multiple of 512 bytes
217-
let padding = [UInt8](repeating: 0, count: 512 - (bytes.count % 512))
227+
let padding = [UInt8](repeating: 0, count: padding(bytes.count))
218228
hdr.append(contentsOf: padding)
219229

220230
// Append the end of file marker

Tests/TarTests/TarUnitTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,26 @@ let trailerLen = 2 * blocksize
4242
#expect(octal11(input) == expected)
4343
}
4444

45+
// We should never add a full block (512 bytes) of padding
46+
@Test(arguments: [
47+
(input: 0, expected: 0),
48+
(input: 1, expected: 511),
49+
(input: 2, expected: 510),
50+
(input: 511, expected: 1),
51+
(input: 512, expected: 0),
52+
(input: 513, expected: 511),
53+
])
54+
func testPadded(input: Int, expected: Int) async throws {
55+
#expect(padding(input) == expected)
56+
}
57+
58+
@Test(arguments: 0...1025)
59+
func testPaddedProperties(input: Int) async throws {
60+
let output = padding(input)
61+
#expect((input + output) % 512 == 0) // The padded output should be a whole number of blocks
62+
#expect(output < 512) // we should never write a full block of padding, because tar considers this to be the end of the file
63+
}
64+
4565
@Test func testUInt8writeString() async throws {
4666
// Fill the buffer with 0xFF to show null termination
4767
var hdr = [UInt8](repeating: 255, count: 21)

0 commit comments

Comments
 (0)