UNPKG

@obsidize/tar-browserify

Version:

Browser-based tar utility for packing and unpacking tar files (stream-capable)

265 lines (264 loc) 9.5 kB
import { TarUtility } from '../common/tar-utility'; import { PaxTarHeader } from './pax/pax-tar-header'; import { UstarHeader } from './ustar/ustar-header'; import { UstarHeaderField } from './ustar/ustar-header-field'; import { UstarHeaderLinkIndicatorType } from './ustar/ustar-header-link-indicator-type'; /** * Facade over a backing Uint8Array buffer to consume/edit header data * at a specific location in the buffer. * * Does not perform any mutations or reads on creation, and * lazy loads/sets data via getters and setters. */ export class TarHeader { constructor(options) { const { ustar, pax, preamble, isPaxGlobal } = options; this.ustar = ustar; this.pax = pax; this.mPreamble = preamble; this.mIsGlobal = !!isPaxGlobal; this.trySyncPaxHeader(); } static isTarHeader(value) { return !!(value && (value instanceof TarHeader)); } /** * @returns A new `TarHeader` instance populated with the content returned by `defaultValues()` */ static seeded() { return TarHeader.from({}); } /** * @returns A new `TarHeader` instance based on the given attributes (if they are a POJO). * Note that if the given value is already a TarHeader instance, this will return it as-is. */ static from(attrs) { if (TarHeader.isTarHeader(attrs)) { return attrs; } const ustar = UstarHeader.from(attrs); const paxRequiredAttributes = TarHeader.collectPaxRequiredAttributes(attrs); let pax; if (paxRequiredAttributes) { // The path property is the only reason we fall back to PAX as of now. // This block may need to be wrapped in a check for the path property if other attributes are added later on. const [directoryName, fileName] = TarHeader.splitBaseFileName(paxRequiredAttributes.path); ustar.fileName = fileName; ustar.fileNamePrefix = directoryName; pax = PaxTarHeader.fromAttributes(paxRequiredAttributes); } return new TarHeader({ ustar, pax }); } /** * Short-hand for constructing a new `TarHeader` and immediately calling `toUint8Array()` on it */ static serialize(attrs) { if (TarHeader.isTarHeader(attrs)) { return attrs.toUint8Array(); } return TarHeader.from(attrs).toUint8Array(); } static collectPaxRequiredAttributes(attrs) { if (TarUtility.isObject(attrs)) { let collected = {}; if (attrs.fileName && attrs.fileName.length > UstarHeaderField.fileName.size) { collected.path = attrs.fileName; } if (Object.keys(collected).length > 0) { return collected; } } return null; } static splitBaseFileName(fileName) { let offset = fileName.lastIndexOf('/'); if (offset >= 0) { return [fileName.substring(0, offset), fileName.substring(offset + 1)]; } offset = fileName.lastIndexOf('\\'); if (offset >= 0) { return [fileName.substring(0, offset), fileName.substring(offset + 1)]; } return ['', fileName]; } get preamble() { return this.mPreamble; } get byteLength() { var _a, _b, _c, _d; const primary = this.ustar.byteLength; const pax = (_b = (_a = this.pax) === null || _a === void 0 ? void 0 : _a.toUint8ArrayPadded().byteLength) !== null && _b !== void 0 ? _b : 0; const preamble = (_d = (_c = this.preamble) === null || _c === void 0 ? void 0 : _c.byteLength) !== null && _d !== void 0 ? _d : 0; return primary + pax + preamble; } get fileName() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.path) || this.ustar.fileName; } get fileMode() { return this.ustar.fileMode; } get ownerUserId() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.userId) || this.ustar.ownerUserId; } get groupUserId() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.groupId) || this.ustar.groupUserId; } get fileSize() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.size) || this.ustar.fileSize; } get lastModified() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.lastModified) || this.ustar.lastModified; } get headerChecksum() { return this.ustar.headerChecksum; } get linkedFileName() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.linkPath) || this.ustar.linkedFileName; } get typeFlag() { return this.ustar.typeFlag; } get ustarIndicator() { return this.ustar.ustarIndicator; } get ustarVersion() { return this.ustar.ustarVersion; } get ownerUserName() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.userName) || this.ustar.ownerUserName; } get ownerGroupName() { var _a; return ((_a = this.pax) === null || _a === void 0 ? void 0 : _a.groupName) || this.ustar.ownerGroupName; } get deviceMajorNumber() { return this.ustar.deviceMajorNumber; } get deviceMinorNumber() { return this.ustar.deviceMinorNumber; } get fileNamePrefix() { return this.ustar.fileNamePrefix; } get isPaxHeader() { return this.isLocalPaxHeader || this.isGlobalPaxHeader; } get isGlobalPaxHeader() { return this.isGlobalPaxPreHeader || this.isGlobalPaxPostHeader; } get isLocalPaxHeader() { return this.isLocalPaxPreHeader || this.isLocalPaxPostHeader; } get isGlobalPaxPreHeader() { return this.ustar.isGlobalPaxHeader; } get isLocalPaxPreHeader() { return this.ustar.isLocalPaxHeader; } get isGlobalPaxPostHeader() { var _a, _b; return (_b = (_a = this.preamble) === null || _a === void 0 ? void 0 : _a.isGlobalPaxHeader) !== null && _b !== void 0 ? _b : false; } get isLocalPaxPostHeader() { var _a, _b; return (_b = (_a = this.preamble) === null || _a === void 0 ? void 0 : _a.isLocalPaxHeader) !== null && _b !== void 0 ? _b : false; } get isFileHeader() { return this.ustar.isFileHeader; } get isDirectoryHeader() { return this.ustar.isDirectoryHeader; } /** * Removes any unknown or un-standardized keys from * the PAX portion of this header (if one exists). * * See also `PaxTarHeader.clean()`. * * @returns `this` for operation chaining */ clean() { var _a; (_a = this.pax) === null || _a === void 0 ? void 0 : _a.clean(); return this; } /** * Ensures that things such as checksum values are * synchronized with the current underlying header states. * * @returns `this` for operation chaining */ normalize() { this.ustar.updateChecksum(); this.trySyncPaxHeader(); return this; } /** * @returns A snapshot of the underlying buffer for this header */ toUint8Array() { this.normalize(); const isPax = !!(this.isPaxHeader && this.pax && this.preamble); if (!isPax) { return this.ustar.bytes; } const preambleBytes = this.preamble.bytes; const paxBytes = this.pax.toUint8ArrayPadded(); const ownBytes = this.ustar.bytes; const totalSize = preambleBytes.byteLength + paxBytes.byteLength + ownBytes.byteLength; const result = new Uint8Array(totalSize); let offset = 0; result.set(this.preamble.bytes, offset); offset += preambleBytes.byteLength; result.set(paxBytes, offset); offset += paxBytes.byteLength; result.set(ownBytes, offset); return result; } toJSON() { const attributes = this.ustar.toAttributes(); const { pax, preamble, ustar } = this; const { bytes, offset } = ustar; const buffer = { byteLength: bytes.byteLength, content: TarUtility.getDebugHexString(bytes) }; return { offset, attributes, buffer, preamble, pax }; } trySyncPaxHeader() { if (!this.pax) { return; } const fileName = this.fileName; const fileSize = this.pax.toUint8Array().byteLength; const lastModified = this.pax.lastModified; const typeFlag = this.mIsGlobal ? UstarHeaderLinkIndicatorType.GLOBAL_EXTENDED_HEADER : UstarHeaderLinkIndicatorType.LOCAL_EXTENDED_HEADER; const preambleAttrs = { fileName, typeFlag, lastModified, fileSize }; if (this.mPreamble) { this.mPreamble.update(preambleAttrs); } else { this.mPreamble = UstarHeader.from(preambleAttrs); } } }