282 lines
7.0 KiB
JavaScript
282 lines
7.0 KiB
JavaScript
var ZEROS = '0000000000000000000'
|
|
var ZERO_OFFSET = '0'.charCodeAt(0)
|
|
var USTAR = 'ustar\x0000'
|
|
var MASK = parseInt('7777', 8)
|
|
|
|
var clamp = function (index, len, defaultValue) {
|
|
if (typeof index !== 'number') return defaultValue
|
|
index = ~~index // Coerce to integer.
|
|
if (index >= len) return len
|
|
if (index >= 0) return index
|
|
index += len
|
|
if (index >= 0) return index
|
|
return 0
|
|
}
|
|
|
|
var toType = function (flag) {
|
|
switch (flag) {
|
|
case 0:
|
|
return 'file'
|
|
case 1:
|
|
return 'link'
|
|
case 2:
|
|
return 'symlink'
|
|
case 3:
|
|
return 'character-device'
|
|
case 4:
|
|
return 'block-device'
|
|
case 5:
|
|
return 'directory'
|
|
case 6:
|
|
return 'fifo'
|
|
case 7:
|
|
return 'contiguous-file'
|
|
case 72:
|
|
return 'pax-header'
|
|
case 55:
|
|
return 'pax-global-header'
|
|
case 27:
|
|
return 'gnu-long-link-path'
|
|
case 28:
|
|
case 30:
|
|
return 'gnu-long-path'
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
var toTypeflag = function (flag) {
|
|
switch (flag) {
|
|
case 'file':
|
|
return 0
|
|
case 'link':
|
|
return 1
|
|
case 'symlink':
|
|
return 2
|
|
case 'character-device':
|
|
return 3
|
|
case 'block-device':
|
|
return 4
|
|
case 'directory':
|
|
return 5
|
|
case 'fifo':
|
|
return 6
|
|
case 'contiguous-file':
|
|
return 7
|
|
case 'pax-header':
|
|
return 72
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
var alloc = function (size) {
|
|
var buf = new Buffer(size)
|
|
buf.fill(0)
|
|
return buf
|
|
}
|
|
|
|
var indexOf = function (block, num, offset, end) {
|
|
for (; offset < end; offset++) {
|
|
if (block[offset] === num) return offset
|
|
}
|
|
return end
|
|
}
|
|
|
|
var cksum = function (block) {
|
|
var sum = 8 * 32
|
|
for (var i = 0; i < 148; i++) sum += block[i]
|
|
for (var j = 156; j < 512; j++) sum += block[j]
|
|
return sum
|
|
}
|
|
|
|
var encodeOct = function (val, n) {
|
|
val = val.toString(8)
|
|
return ZEROS.slice(0, n - val.length) + val + ' '
|
|
}
|
|
|
|
/* Copied from the node-tar repo and modified to meet
|
|
* tar-stream coding standard.
|
|
*
|
|
* Source: https://github.com/npm/node-tar/blob/51b6627a1f357d2eb433e7378e5f05e83b7aa6cd/lib/header.js#L349
|
|
*/
|
|
function parse256 (buf) {
|
|
// first byte MUST be either 80 or FF
|
|
// 80 for positive, FF for 2's comp
|
|
var positive
|
|
if (buf[0] === 0x80) positive = true
|
|
else if (buf[0] === 0xFF) positive = false
|
|
else return null
|
|
|
|
// build up a base-256 tuple from the least sig to the highest
|
|
var zero = false
|
|
var tuple = []
|
|
for (var i = buf.length - 1; i > 0; i--) {
|
|
var byte = buf[i]
|
|
if (positive) tuple.push(byte)
|
|
else if (zero && byte === 0) tuple.push(0)
|
|
else if (zero) {
|
|
zero = false
|
|
tuple.push(0x100 - byte)
|
|
} else tuple.push(0xFF - byte)
|
|
}
|
|
|
|
var sum = 0
|
|
var l = tuple.length
|
|
for (i = 0; i < l; i++) {
|
|
sum += tuple[i] * Math.pow(256, i)
|
|
}
|
|
|
|
return positive ? sum : -1 * sum
|
|
}
|
|
|
|
var decodeOct = function (val, offset) {
|
|
// If prefixed with 0x80 then parse as a base-256 integer
|
|
if (val[offset] & 0x80) {
|
|
return parse256(val.slice(offset, offset + 8))
|
|
} else {
|
|
// Older versions of tar can prefix with spaces
|
|
while (offset < val.length && val[offset] === 32) offset++
|
|
var end = clamp(indexOf(val, 32, offset, val.length), val.length, val.length)
|
|
while (offset < end && val[offset] === 0) offset++
|
|
if (end === offset) return 0
|
|
return parseInt(val.slice(offset, end).toString(), 8)
|
|
}
|
|
}
|
|
|
|
var decodeStr = function (val, offset, length) {
|
|
return val.slice(offset, indexOf(val, 0, offset, offset + length)).toString()
|
|
}
|
|
|
|
var addLength = function (str) {
|
|
var len = Buffer.byteLength(str)
|
|
var digits = Math.floor(Math.log(len) / Math.log(10)) + 1
|
|
if (len + digits > Math.pow(10, digits)) digits++
|
|
|
|
return (len + digits) + str
|
|
}
|
|
|
|
exports.decodeLongPath = function (buf) {
|
|
return decodeStr(buf, 0, buf.length)
|
|
}
|
|
|
|
exports.encodePax = function (opts) { // TODO: encode more stuff in pax
|
|
var result = ''
|
|
if (opts.name) result += addLength(' path=' + opts.name + '\n')
|
|
if (opts.linkname) result += addLength(' linkpath=' + opts.linkname + '\n')
|
|
var pax = opts.pax
|
|
if (pax) {
|
|
for (var key in pax) {
|
|
result += addLength(' ' + key + '=' + pax[key] + '\n')
|
|
}
|
|
}
|
|
return new Buffer(result)
|
|
}
|
|
|
|
exports.decodePax = function (buf) {
|
|
var result = {}
|
|
|
|
while (buf.length) {
|
|
var i = 0
|
|
while (i < buf.length && buf[i] !== 32) i++
|
|
var len = parseInt(buf.slice(0, i).toString(), 10)
|
|
if (!len) return result
|
|
|
|
var b = buf.slice(i + 1, len - 1).toString()
|
|
var keyIndex = b.indexOf('=')
|
|
if (keyIndex === -1) return result
|
|
result[b.slice(0, keyIndex)] = b.slice(keyIndex + 1)
|
|
|
|
buf = buf.slice(len)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
exports.encode = function (opts) {
|
|
var buf = alloc(512)
|
|
var name = opts.name
|
|
var prefix = ''
|
|
|
|
if (opts.typeflag === 5 && name[name.length - 1] !== '/') name += '/'
|
|
if (Buffer.byteLength(name) !== name.length) return null // utf-8
|
|
|
|
while (Buffer.byteLength(name) > 100) {
|
|
var i = name.indexOf('/')
|
|
if (i === -1) return null
|
|
prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i)
|
|
name = name.slice(i + 1)
|
|
}
|
|
|
|
if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null
|
|
if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null
|
|
|
|
buf.write(name)
|
|
buf.write(encodeOct(opts.mode & MASK, 6), 100)
|
|
buf.write(encodeOct(opts.uid, 6), 108)
|
|
buf.write(encodeOct(opts.gid, 6), 116)
|
|
buf.write(encodeOct(opts.size, 11), 124)
|
|
buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136)
|
|
|
|
buf[156] = ZERO_OFFSET + toTypeflag(opts.type)
|
|
|
|
if (opts.linkname) buf.write(opts.linkname, 157)
|
|
|
|
buf.write(USTAR, 257)
|
|
if (opts.uname) buf.write(opts.uname, 265)
|
|
if (opts.gname) buf.write(opts.gname, 297)
|
|
buf.write(encodeOct(opts.devmajor || 0, 6), 329)
|
|
buf.write(encodeOct(opts.devminor || 0, 6), 337)
|
|
|
|
if (prefix) buf.write(prefix, 345)
|
|
|
|
buf.write(encodeOct(cksum(buf), 6), 148)
|
|
|
|
return buf
|
|
}
|
|
|
|
exports.decode = function (buf) {
|
|
var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET
|
|
|
|
var name = decodeStr(buf, 0, 100)
|
|
var mode = decodeOct(buf, 100)
|
|
var uid = decodeOct(buf, 108)
|
|
var gid = decodeOct(buf, 116)
|
|
var size = decodeOct(buf, 124)
|
|
var mtime = decodeOct(buf, 136)
|
|
var type = toType(typeflag)
|
|
var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100)
|
|
var uname = decodeStr(buf, 265, 32)
|
|
var gname = decodeStr(buf, 297, 32)
|
|
var devmajor = decodeOct(buf, 329)
|
|
var devminor = decodeOct(buf, 337)
|
|
|
|
if (buf[345]) name = decodeStr(buf, 345, 155) + '/' + 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.
|
|
if (c === 8 * 32) return null
|
|
|
|
// valid checksum
|
|
if (c !== decodeOct(buf, 148)) throw new Error('Invalid tar header. Maybe the tar is corrupted or it needs to be gunzipped?')
|
|
|
|
return {
|
|
name: name,
|
|
mode: mode,
|
|
uid: uid,
|
|
gid: gid,
|
|
size: size,
|
|
mtime: new Date(1000 * mtime),
|
|
type: type,
|
|
linkname: linkname,
|
|
uname: uname,
|
|
gname: gname,
|
|
devmajor: devmajor,
|
|
devminor: devminor
|
|
}
|
|
}
|