| 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 |
| } |
| } |