You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
7.2 KiB
283 lines
7.2 KiB
var toBuffer = require('to-buffer') |
|
var alloc = require('buffer-alloc') |
|
|
|
var ZEROS = '0000000000000000000' |
|
var SEVENS = '7777777777777777777' |
|
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 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) |
|
if (val.length > n) return SEVENS.slice(0, n) + ' ' |
|
else 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, length) { |
|
val = val.slice(offset, offset + length) |
|
offset = 0 |
|
|
|
// If prefixed with 0x80 then parse as a base-256 integer |
|
if (val[offset] & 0x80) { |
|
return parse256(val) |
|
} 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, encoding) { |
|
return val.slice(offset, indexOf(val, 0, offset, offset + length)).toString(encoding) |
|
} |
|
|
|
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, encoding) { |
|
return decodeStr(buf, 0, buf.length, encoding) |
|
} |
|
|
|
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 toBuffer(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, filenameEncoding) { |
|
var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET |
|
|
|
var name = decodeStr(buf, 0, 100, filenameEncoding) |
|
var mode = decodeOct(buf, 100, 8) |
|
var uid = decodeOct(buf, 108, 8) |
|
var gid = decodeOct(buf, 116, 8) |
|
var size = decodeOct(buf, 124, 12) |
|
var mtime = decodeOct(buf, 136, 12) |
|
var type = toType(typeflag) |
|
var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100, filenameEncoding) |
|
var uname = decodeStr(buf, 265, 32) |
|
var gname = decodeStr(buf, 297, 32) |
|
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. |
|
if (c === 8 * 32) return null |
|
|
|
// 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?') |
|
|
|
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 |
|
} |
|
}
|
|
|