Improve gnu/oldgnu format support
- gnu/oldgnu formats do not have a prefix field. - Explicitly validate magic and version when parsing. Throw an exception if unsupported/invalid magic is encountered.
This commit is contained in:
parent
b6392bdc8a
commit
6dcda69247
6 changed files with 118 additions and 7 deletions
30
headers.js
30
headers.js
|
@ -3,8 +3,13 @@ var alloc = Buffer.alloc
|
|||
var ZEROS = '0000000000000000000'
|
||||
var SEVENS = '7777777777777777777'
|
||||
var ZERO_OFFSET = '0'.charCodeAt(0)
|
||||
var USTAR = 'ustar\x0000'
|
||||
var USTAR_MAGIC = Buffer.from('ustar\x00', 'binary')
|
||||
var USTAR_VER = Buffer.from('00', 'binary')
|
||||
var GNU_MAGIC = Buffer.from('ustar\x20', 'binary')
|
||||
var GNU_VER = Buffer.from('\x20\x00', 'binary')
|
||||
var MASK = parseInt('7777', 8)
|
||||
var MAGIC_OFFSET = 257
|
||||
var VERSION_OFFSET = 263
|
||||
|
||||
var clamp = function (index, len, defaultValue) {
|
||||
if (typeof index !== 'number') return defaultValue
|
||||
|
@ -223,7 +228,8 @@ exports.encode = function (opts) {
|
|||
|
||||
if (opts.linkname) buf.write(opts.linkname, 157)
|
||||
|
||||
buf.write(USTAR, 257)
|
||||
USTAR_MAGIC.copy(buf, MAGIC_OFFSET)
|
||||
USTAR_VER.copy(buf, VERSION_OFFSET)
|
||||
if (opts.uname) buf.write(opts.uname, 265)
|
||||
if (opts.gname) buf.write(opts.gname, 297)
|
||||
buf.write(encodeOct(opts.devmajor || 0, 6), 329)
|
||||
|
@ -252,11 +258,6 @@ exports.decode = function (buf, filenameEncoding) {
|
|||
var devmajor = decodeOct(buf, 329, 8)
|
||||
var devminor = decodeOct(buf, 337, 8)
|
||||
|
||||
if (buf[345]) name = decodeStr(buf, 345, 155, filenameEncoding) + '/' + name
|
||||
|
||||
// to support old tar versions that use trailing / to indicate dirs
|
||||
if (typeflag === 0 && name && name[name.length - 1] === '/') typeflag = 5
|
||||
|
||||
var c = cksum(buf)
|
||||
|
||||
// checksum is still initial value if header was null.
|
||||
|
@ -265,6 +266,21 @@ exports.decode = function (buf, filenameEncoding) {
|
|||
// valid checksum
|
||||
if (c !== decodeOct(buf, 148, 8)) throw new Error('Invalid tar header. Maybe the tar is corrupted or it needs to be gunzipped?')
|
||||
|
||||
if (USTAR_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0) {
|
||||
// ustar (posix) format.
|
||||
// prepend prefix, if present.
|
||||
if (buf[345]) name = decodeStr(buf, 345, 155, filenameEncoding) + '/' + name
|
||||
} else if (GNU_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0 &&
|
||||
GNU_VER.compare(buf, VERSION_OFFSET, VERSION_OFFSET + 2) === 0) {
|
||||
// 'gnu'/'oldgnu' format. Similar to ustar, but has support for incremental and
|
||||
// multi-volume tarballs.
|
||||
} else {
|
||||
throw new Error('Invalid tar header: unknown format.')
|
||||
}
|
||||
|
||||
// to support old tar versions that use trailing / to indicate dirs
|
||||
if (typeflag === 0 && name && name[name.length - 1] === '/') typeflag = 5
|
||||
|
||||
return {
|
||||
name: name,
|
||||
mode: mode,
|
||||
|
|
|
@ -590,3 +590,92 @@ test('incomplete', function (t) {
|
|||
|
||||
extract.end(fs.readFileSync(fixtures.INCOMPLETE_TAR))
|
||||
})
|
||||
|
||||
test('gnu', function (t) { // can correctly unpack gnu-tar format
|
||||
t.plan(3)
|
||||
|
||||
var extract = tar.extract()
|
||||
var noEntries = false
|
||||
|
||||
extract.on('entry', function (header, stream, callback) {
|
||||
t.deepEqual(header, {
|
||||
name: 'test.txt',
|
||||
mode: parseInt('644', 8),
|
||||
uid: 12345,
|
||||
gid: 67890,
|
||||
size: 14,
|
||||
mtime: new Date(1559239869000),
|
||||
type: 'file',
|
||||
linkname: null,
|
||||
uname: 'myuser',
|
||||
gname: 'mygroup',
|
||||
devmajor: 0,
|
||||
devminor: 0
|
||||
})
|
||||
|
||||
stream.pipe(concat(function (data) {
|
||||
noEntries = true
|
||||
t.same(data.toString(), 'Hello, world!\n')
|
||||
callback()
|
||||
}))
|
||||
})
|
||||
|
||||
extract.on('finish', function () {
|
||||
t.ok(noEntries)
|
||||
})
|
||||
|
||||
extract.end(fs.readFileSync(fixtures.GNU_TAR))
|
||||
})
|
||||
|
||||
test('gnu-incremental', function (t) {
|
||||
// can correctly unpack gnu-tar incremental format. In this situation,
|
||||
// the tarball will have additional ctime and atime values in the header,
|
||||
// and without awareness of the 'gnu' tar format, the atime (offset 345) is mistaken
|
||||
// for a directory prefix (also offset 345).
|
||||
t.plan(3)
|
||||
|
||||
var extract = tar.extract()
|
||||
var noEntries = false
|
||||
|
||||
extract.on('entry', function (header, stream, callback) {
|
||||
t.deepEqual(header, {
|
||||
name: 'test.txt',
|
||||
mode: parseInt('644', 8),
|
||||
uid: 12345,
|
||||
gid: 67890,
|
||||
size: 14,
|
||||
mtime: new Date(1559239869000),
|
||||
type: 'file',
|
||||
linkname: null,
|
||||
uname: 'myuser',
|
||||
gname: 'mygroup',
|
||||
devmajor: 0,
|
||||
devminor: 0
|
||||
})
|
||||
|
||||
stream.pipe(concat(function (data) {
|
||||
noEntries = true
|
||||
t.same(data.toString(), 'Hello, world!\n')
|
||||
callback()
|
||||
}))
|
||||
})
|
||||
|
||||
extract.on('finish', function () {
|
||||
t.ok(noEntries)
|
||||
})
|
||||
|
||||
extract.end(fs.readFileSync(fixtures.GNU_INCREMENTAL_TAR))
|
||||
})
|
||||
|
||||
test('v7 unsupported', function (t) { // correctly fails to parse v7 tarballs
|
||||
t.plan(1)
|
||||
|
||||
var extract = tar.extract()
|
||||
|
||||
extract.on('error', function (err) {
|
||||
t.ok(!!err)
|
||||
extract.destroy()
|
||||
})
|
||||
|
||||
extract.end(fs.readFileSync(fixtures.V7_TAR))
|
||||
})
|
||||
|
|
BIN
test/fixtures/gnu-incremental.tar
vendored
Normal file
BIN
test/fixtures/gnu-incremental.tar
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/gnu.tar
vendored
Normal file
BIN
test/fixtures/gnu.tar
vendored
Normal file
Binary file not shown.
6
test/fixtures/index.js
vendored
6
test/fixtures/index.js
vendored
|
@ -17,3 +17,9 @@ exports.BASE_256_SIZE = path.join(__dirname, 'base-256-size.tar')
|
|||
exports.HUGE = path.join(__dirname, 'huge.tar.gz')
|
||||
exports.LATIN1_TAR = path.join(__dirname, 'latin1.tar')
|
||||
exports.INCOMPLETE_TAR = path.join(__dirname, 'incomplete.tar')
|
||||
// Created using gnu tar: tar cf gnu-incremental.tar --format gnu --owner=myuser:12345 --group=mygroup:67890 test.txt
|
||||
exports.GNU_TAR = path.join(__dirname, 'gnu.tar')
|
||||
// Created using gnu tar: tar cf gnu-incremental.tar -G --format gnu --owner=myuser:12345 --group=mygroup:67890 test.txt
|
||||
exports.GNU_INCREMENTAL_TAR = path.join(__dirname, 'gnu-incremental.tar')
|
||||
// Created using gnu tar: tar cf v7.tar --format v7 test.txt
|
||||
exports.V7_TAR = path.join(__dirname, 'v7.tar')
|
||||
|
|
BIN
test/fixtures/v7.tar
vendored
Normal file
BIN
test/fixtures/v7.tar
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue