UNPKG

@obsidize/tar-browserify

Version:

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

267 lines (266 loc) 9.71 kB
import { Constants } from '../../common/constants'; import { TarUtility } from '../../common/tar-utility'; import { TarHeaderUtility } from '../tar-header-utility'; import { UstarHeaderField } from './ustar-header-field'; import { UstarHeaderLinkIndicatorType } from './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 UstarHeader { constructor(bytes = new Uint8Array(Constants.HEADER_SIZE), offset = 0) { this.bytes = bytes; this.offset = offset; } /** * @returns A copy of the defaults used by all headers */ static defaultValues() { return { fileName: '', fileMode: Constants.FILE_MODE_DEFAULT, groupUserId: 0, ownerUserId: 0, fileSize: 0, lastModified: TarUtility.getUstarTimestamp(), headerChecksum: 0, linkedFileName: '', typeFlag: UstarHeaderLinkIndicatorType.NORMAL_FILE, ustarIndicator: Constants.USTAR_INDICATOR_VALUE, ustarVersion: Constants.USTAR_VERSION_VALUE, ownerUserName: '', ownerGroupName: '', deviceMajorNumber: '00', deviceMinorNumber: '00', fileNamePrefix: '' }; } static isUstarHeader(value) { return !!(value && (value instanceof UstarHeader)); } /** * @returns A new `UstarHeader` instance based on the given attributes (if they are a POJO). * Note that if the given value is already a UstarHeader instance, this will return it as-is. */ static from(attrs) { if (UstarHeader.isUstarHeader(attrs)) { return attrs; } return new UstarHeader().initialize(attrs); } /** * Short-hand for constructing a new `UstarHeader` and immediately calling `toUint8Array()` on it */ static serialize(attrs) { if (UstarHeader.isUstarHeader(attrs)) { return attrs.toUint8Array(); } return UstarHeader.from(attrs).toUint8Array(); } /** * @returns A new `UstarHeader` instance populated with the content returned by `defaultValues()` */ static seeded() { return UstarHeader.from({}); } get byteLength() { return this.bytes.byteLength; } get fileName() { return UstarHeaderField.fileName.readFrom(this.bytes, this.offset); } set fileName(value) { UstarHeaderField.fileName.writeTo(this.bytes, this.offset, value); } get fileMode() { return UstarHeaderField.fileMode.readFrom(this.bytes, this.offset); } set fileMode(value) { UstarHeaderField.fileMode.writeTo(this.bytes, this.offset, value); } get ownerUserId() { return UstarHeaderField.ownerUserId.readFrom(this.bytes, this.offset); } set ownerUserId(value) { UstarHeaderField.ownerUserId.writeTo(this.bytes, this.offset, value); } get groupUserId() { return UstarHeaderField.groupUserId.readFrom(this.bytes, this.offset); } set groupUserId(value) { UstarHeaderField.groupUserId.writeTo(this.bytes, this.offset, value); } get fileSize() { return UstarHeaderField.fileSize.readFrom(this.bytes, this.offset); } set fileSize(value) { UstarHeaderField.fileSize.writeTo(this.bytes, this.offset, value); } get lastModified() { return UstarHeaderField.lastModified.readFrom(this.bytes, this.offset); } set lastModified(value) { UstarHeaderField.lastModified.writeTo(this.bytes, this.offset, value); } get headerChecksum() { return UstarHeaderField.headerChecksum.readFrom(this.bytes, this.offset); } set headerChecksum(value) { UstarHeaderField.headerChecksum.writeTo(this.bytes, this.offset, value); } get linkedFileName() { return UstarHeaderField.linkedFileName.readFrom(this.bytes, this.offset); } set linkedFileName(value) { UstarHeaderField.linkedFileName.writeTo(this.bytes, this.offset, value); } get typeFlag() { return UstarHeaderField.typeFlag.readFrom(this.bytes, this.offset) || UstarHeaderLinkIndicatorType.UNKNOWN; } set typeFlag(value) { UstarHeaderField.typeFlag.writeTo(this.bytes, this.offset, value); } get ustarIndicator() { return UstarHeaderField.ustarIndicator.readFrom(this.bytes, this.offset); } get ustarVersion() { return UstarHeaderField.ustarVersion.readFrom(this.bytes, this.offset); } set ustarVersion(value) { UstarHeaderField.ustarVersion.writeTo(this.bytes, this.offset, value); } get ownerUserName() { return UstarHeaderField.ownerUserName.readFrom(this.bytes, this.offset); } set ownerUserName(value) { UstarHeaderField.ownerUserName.writeTo(this.bytes, this.offset, value); } get ownerGroupName() { return UstarHeaderField.ownerGroupName.readFrom(this.bytes, this.offset); } set ownerGroupName(value) { UstarHeaderField.ownerGroupName.writeTo(this.bytes, this.offset, value); } get deviceMajorNumber() { return UstarHeaderField.deviceMajorNumber.readFrom(this.bytes, this.offset); } set deviceMajorNumber(value) { UstarHeaderField.deviceMajorNumber.writeTo(this.bytes, this.offset, value); } get deviceMinorNumber() { return UstarHeaderField.deviceMinorNumber.readFrom(this.bytes, this.offset); } set deviceMinorNumber(value) { UstarHeaderField.deviceMinorNumber.writeTo(this.bytes, this.offset, value); } get fileNamePrefix() { return UstarHeaderField.fileNamePrefix.readFrom(this.bytes, this.offset); } set fileNamePrefix(value) { UstarHeaderField.fileNamePrefix.writeTo(this.bytes, this.offset, value); } get isPaxHeader() { return this.isLocalPaxHeader || this.isGlobalPaxHeader; } get isGlobalPaxHeader() { return this.typeFlag === UstarHeaderLinkIndicatorType.GLOBAL_EXTENDED_HEADER; } get isLocalPaxHeader() { return this.typeFlag === UstarHeaderLinkIndicatorType.LOCAL_EXTENDED_HEADER; } get isFileHeader() { return TarHeaderUtility.isTarHeaderLinkIndicatorTypeFile(this.typeFlag); } get isDirectoryHeader() { return TarHeaderUtility.isTarHeaderLinkIndicatorTypeDirectory(this.typeFlag); } /** * @returns A snapshot of the underlying buffer for this header */ toUint8Array() { this.updateChecksum(); return this.bytes.slice(this.offset, this.offset + Constants.HEADER_SIZE); } toJSON() { const attributes = this.toAttributes(); const { bytes, offset } = this; const buffer = { byteLength: bytes.byteLength, content: TarUtility.getDebugHexString(bytes) }; return { offset, attributes, buffer }; } toAttributes() { const { fileName, fileMode, groupUserId, ownerUserId, fileSize, lastModified, headerChecksum, linkedFileName, typeFlag, ustarIndicator, ustarVersion, ownerUserName, ownerGroupName, deviceMajorNumber, deviceMinorNumber, fileNamePrefix } = this; return { fileName, fileMode, groupUserId, ownerUserId, fileSize, lastModified, headerChecksum, linkedFileName, typeFlag, ustarIndicator, ustarVersion, ownerUserName, ownerGroupName, deviceMajorNumber, deviceMinorNumber, fileNamePrefix }; } /** * Override all values in the header. * Any values not provided in `attrs` will be filled in with default values. * @returns `this` for operation chaining */ initialize(attrs = {}) { const completeAttrs = Object.assign(UstarHeader.defaultValues(), (attrs || {})); this.update(completeAttrs); return this; } /** * Ensures the state of the header is synced after changes have been made. * @returns `this` for operation chaining */ updateChecksum() { let checksum = TarHeaderUtility.CHECKSUM_SEED; for (const field of TarHeaderUtility.CHECKSUM_FIELDS) { checksum += field.calculateChecksum(this.bytes, this.offset); } this.headerChecksum = checksum; return this; } /** * Mechanism to batch-update properties. * Automatically normalizes the header if any changes were made. * @returns `this` for operation chaining */ update(attrs) { if (!attrs) { return this; } let didModifyAnyField = false; for (const field of TarHeaderUtility.ALL_FIELDS) { const value = attrs[field.name]; if (TarUtility.isDefined(value)) { const modified = field.writeTo(this.bytes, this.offset, value); didModifyAnyField = didModifyAnyField || modified; } } if (didModifyAnyField) { this.updateChecksum(); } return this; } }