tar-iterator
Version:
Extract contents from tar archive type using an iterator API using streams or paths. Use stream interface and pipe transforms to add decompression algorithms
136 lines • 5.04 kB
JavaScript
/**
* Extensions - GNU/PAX extension handling for TAR
*
* Manages state and decoding for:
* - GNU LongPath (type 'L') - paths > 100 chars
* - GNU LongLink (type 'K') - symlink targets > 100 chars
* - PAX Headers (type 'x') - extended attributes per-entry
* - PAX Global Headers (type 'g') - extended attributes for all entries
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get applyExtensions () {
return applyExtensions;
},
get applyPaxToHeader () {
return applyPaxToHeader;
},
get concatBuffers () {
return concatBuffers;
},
get createExtensionState () {
return createExtensionState;
},
get finalizeExtension () {
return finalizeExtension;
}
});
var _extractbaseiterator = require("extract-base-iterator");
var _headersts = require("./headers.js");
function createExtensionState() {
return {
gnuLongPath: null,
gnuLongLink: null,
paxHeader: null,
paxGlobal: {},
extensionData: [],
extensionRemaining: 0
};
}
function finalizeExtension(state, currentState, header, encoding) {
// Import state constants
var STATE_GNU_LONG_PATH = 4;
var STATE_GNU_LONG_LINK = 5;
var STATE_PAX_HEADER = 6;
// Concatenate all collected data
var combined = concatBuffers(state.extensionData);
state.extensionData = [];
switch(currentState){
case STATE_GNU_LONG_PATH:
state.gnuLongPath = (0, _headersts.decodeLongPath)(combined, encoding);
break;
case STATE_GNU_LONG_LINK:
state.gnuLongLink = (0, _headersts.decodeLongPath)(combined, encoding);
break;
case STATE_PAX_HEADER:
// Check if this was a global header
if (header && header.type === 'pax-global-header') {
var global = (0, _headersts.decodePax)(combined);
// Merge into global (don't replace, merge)
for(var key in global){
// biome-ignore lint/suspicious/noPrototypeBuiltins: ES2021 compatibility
if (global.hasOwnProperty(key)) {
state.paxGlobal[key] = global[key];
}
}
} else {
state.paxHeader = (0, _headersts.decodePax)(combined);
}
break;
}
}
function applyExtensions(header, state) {
// Apply PAX global header first
if (state.paxGlobal) {
applyPaxToHeader(header, state.paxGlobal);
}
// Apply PAX header (per-entry, overrides global)
if (state.paxHeader) {
applyPaxToHeader(header, state.paxHeader);
header.pax = state.paxHeader;
state.paxHeader = null;
}
// Apply GNU long path (overrides PAX path)
if (state.gnuLongPath !== null) {
header.name = state.gnuLongPath;
state.gnuLongPath = null;
}
// Apply GNU long link (overrides PAX linkpath)
if (state.gnuLongLink !== null) {
header.linkname = state.gnuLongLink;
state.gnuLongLink = null;
}
// Handle old tar versions that use trailing / to indicate directories
// This check is done AFTER extensions are applied so we use the final
// resolved name (GNU long path or PAX path), not the truncated name field.
if (header.type === 'file' && header.name && header.name[header.name.length - 1] === '/') {
header.type = 'directory';
}
}
function applyPaxToHeader(header, pax) {
if (pax.path) header.name = pax.path;
if (pax.linkpath) header.linkname = pax.linkpath;
if (pax.size) header.size = parseInt(pax.size, 10);
if (pax.uid) header.uid = parseInt(pax.uid, 10);
if (pax.gid) header.gid = parseInt(pax.gid, 10);
if (pax.uname) header.uname = pax.uname;
if (pax.gname) header.gname = pax.gname;
if (pax.mtime) header.mtime = new Date(parseFloat(pax.mtime) * 1000);
}
function concatBuffers(buffers) {
if (buffers.length === 0) return Buffer.alloc ? Buffer.alloc(0) : new Buffer(0);
if (buffers.length === 1) return buffers[0];
var totalLength = buffers.reduce(function(sum, buf) {
return sum + buf.length;
}, 0);
if (Buffer.concat) {
return Buffer.concat(buffers, totalLength);
}
// Node 0.8 fallback
var result = (0, _extractbaseiterator.allocBufferUnsafe)(totalLength);
var offset = 0;
for(var i = 0; i < buffers.length; i++){
buffers[i].copy(result, offset);
offset += buffers[i].length;
}
return result;
}
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }