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.
		
		
		
		
		
			
		
			
				
					296 lines
				
				7.8 KiB
			
		
		
			
		
	
	
					296 lines
				
				7.8 KiB
			| 
											2 years ago
										 | var alloc = Buffer.alloc | ||
|  | 
 | ||
|  | var ZEROS = '0000000000000000000' | ||
|  | var SEVENS = '7777777777777777777' | ||
|  | var ZERO_OFFSET = '0'.charCodeAt(0) | ||
|  | 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 | ||
|  |   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 tuple = [] | ||
|  |   for (var i = buf.length - 1; i > 0; i--) { | ||
|  |     var byte = buf[i] | ||
|  |     if (positive) tuple.push(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 Buffer.from(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) | ||
|  | 
 | ||
|  |   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) | ||
|  |   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, allowUnknownFormat) { | ||
|  |   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) | ||
|  | 
 | ||
|  |   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?') | ||
|  | 
 | ||
|  |   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 { | ||
|  |     if (!allowUnknownFormat) { | ||
|  |       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, | ||
|  |     mode, | ||
|  |     uid, | ||
|  |     gid, | ||
|  |     size, | ||
|  |     mtime: new Date(1000 * mtime), | ||
|  |     type, | ||
|  |     linkname, | ||
|  |     uname, | ||
|  |     gname, | ||
|  |     devmajor, | ||
|  |     devminor | ||
|  |   } | ||
|  | } |