@obsidize/tar-browserify
Version:
Browser-based tar utility for packing and unpacking tar files (stream-capable)
238 lines (237 loc) • 8.55 kB
JavaScript
import { Constants } from '../../common/constants';
import { TarUtility } from '../../common/tar-utility';
import { UstarHeaderFieldTransformType } from './ustar-header-field-transform';
import { UstarHeaderFieldType } from './ustar-header-field-type';
/**
* Definitions taken from here:
* https://en.wikipedia.org/wiki/Tar_(computing)
*/
export class UstarHeaderField {
constructor(config) {
this.name = config.name;
this.offset = config.offset;
this.size = config.size;
this.type = config.type;
this.constantValue = config.constantValue || undefined;
this.transform = UstarHeaderFieldTransformType.from(this.type);
}
static frozen(config) {
return Object.freeze(new UstarHeaderField(config));
}
static all() {
return [
UstarHeaderField.fileName,
UstarHeaderField.fileMode,
UstarHeaderField.ownerUserId,
UstarHeaderField.groupUserId,
UstarHeaderField.fileSize,
UstarHeaderField.lastModified,
UstarHeaderField.headerChecksum,
UstarHeaderField.typeFlag,
UstarHeaderField.linkedFileName,
UstarHeaderField.ustarIndicator,
UstarHeaderField.ustarVersion,
UstarHeaderField.ownerUserName,
UstarHeaderField.ownerGroupName,
UstarHeaderField.deviceMajorNumber,
UstarHeaderField.deviceMinorNumber,
UstarHeaderField.fileNamePrefix,
];
}
static checksumSet() {
return UstarHeaderField.all().filter((v) => v !== UstarHeaderField.headerChecksum);
}
// =====================================================================
// Instance Methods
// =====================================================================
/**
* Shorthand for padding the output of `slice` into `decodeString`.
*/
sliceString(input, offset) {
return TarUtility.decodeString(this.slice(input, offset));
}
/**
* @param input - a buffer of one or more complete tar sectors
* @param offset - the offset to slice from (must be a multiple of `SECTOR_SIZE`)
* @returns the slice of the given input Uint8Array that this field resides in.
*/
slice(input, offset = 0) {
if (!TarUtility.isUint8Array(input)) {
return new Uint8Array(0);
}
const start = offset + this.offset;
const end = start + this.size;
return input.slice(start, end);
}
/**
* @param input - a buffer of one or more complete tar sectors
* @returns The value parsed from the input based on this field's transform type,
* or `undefined` on error.
*/
deserialize(input, offset = 0) {
if (TarUtility.isUint8Array(input)) {
return this.transform.deserialize(input, this.size, offset);
}
return undefined;
}
/**
* @param input - the value to be serialized, based on this field's transform type.
* @returns the serialized value as a Uint8Array
*/
serialize(input) {
const result = new Uint8Array(this.size);
const value = this.transform.serialize(input, this.size);
result.set(value, 0);
return result;
}
/**
* Runs `deserialize()` while also taking this field's offset into account.
*/
readFrom(input, offset) {
return this.deserialize(input, offset + this.offset);
}
/**
* Serialize the given value and set the output bytes in the given output buffer.
* @param output - the output buffer to be written to
* @param headerOffset - the offset of the header in the output buffer to insert the update.
* Note that this field's offset will be added to the header offset when inserting.
* @param value - the value to be serialized
* @returns true if the buffer was updated
*/
writeTo(output, headerOffset, value) {
headerOffset = Math.max(headerOffset, 0);
const valueBytes = this.serialize(value);
const valueByteLength = valueBytes.byteLength;
const absoluteOffset = headerOffset + this.offset;
if (valueByteLength > 0 &&
TarUtility.isUint8Array(output) &&
output.byteLength >= absoluteOffset + valueByteLength) {
output.set(valueBytes, absoluteOffset);
return true;
}
return false;
}
/**
* Calculates the checksum value for this field in the given input buffer, at the given offset.
* All field checksum values are aggregated together to form the main header checksum entry.
* @param input - the input buffer to extract a field checksum from
* @param offset - the offset of the header in the buffer (will be combined with this field's offset)
* @returns the checksum value for this specific field
*/
calculateChecksum(input, offset = 0) {
let checksum = 0;
if (!TarUtility.isUint8Array(input)) {
return checksum;
}
const start = offset + this.offset;
const end = start + this.size;
for (let i = start; i < end; i++) {
checksum += input[i];
}
return checksum;
}
}
// =====================================================================
// Legacy Fields
// =====================================================================
UstarHeaderField.fileName = UstarHeaderField.frozen({
name: 'fileName',
offset: 0,
size: 100,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
UstarHeaderField.fileMode = UstarHeaderField.frozen({
name: 'fileMode',
offset: 100,
size: 8,
type: UstarHeaderFieldType.INTEGER_OCTAL,
});
UstarHeaderField.ownerUserId = UstarHeaderField.frozen({
name: 'ownerUserId',
offset: 108,
size: 8,
type: UstarHeaderFieldType.INTEGER_OCTAL,
});
UstarHeaderField.groupUserId = UstarHeaderField.frozen({
name: 'groupUserId',
offset: 116,
size: 8,
type: UstarHeaderFieldType.INTEGER_OCTAL,
});
UstarHeaderField.fileSize = UstarHeaderField.frozen({
name: 'fileSize',
offset: 124,
size: 12,
type: UstarHeaderFieldType.INTEGER_OCTAL,
});
UstarHeaderField.lastModified = UstarHeaderField.frozen({
name: 'lastModified',
offset: 136,
size: 12,
type: UstarHeaderFieldType.INTEGER_OCTAL_TIMESTAMP,
});
UstarHeaderField.headerChecksum = UstarHeaderField.frozen({
name: 'headerChecksum',
offset: 148,
size: 8,
type: UstarHeaderFieldType.INTEGER_OCTAL,
});
UstarHeaderField.typeFlag = UstarHeaderField.frozen({
name: 'typeFlag',
offset: 156,
size: 1,
type: UstarHeaderFieldType.ASCII,
});
UstarHeaderField.linkedFileName = UstarHeaderField.frozen({
name: 'linkedFileName',
offset: 157,
size: 100,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
// =====================================================================
// USTAR Fields
// =====================================================================
UstarHeaderField.ustarIndicator = UstarHeaderField.frozen({
name: 'ustarIndicator',
offset: 257,
size: 6,
type: UstarHeaderFieldType.ASCII,
constantValue: Constants.USTAR_INDICATOR_VALUE,
});
UstarHeaderField.ustarVersion = UstarHeaderField.frozen({
name: 'ustarVersion',
offset: 263,
size: 2,
type: UstarHeaderFieldType.ASCII,
constantValue: Constants.USTAR_VERSION_VALUE,
});
UstarHeaderField.ownerUserName = UstarHeaderField.frozen({
name: 'ownerUserName',
offset: 265,
size: 32,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
UstarHeaderField.ownerGroupName = UstarHeaderField.frozen({
name: 'ownerGroupName',
offset: 297,
size: 32,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
UstarHeaderField.deviceMajorNumber = UstarHeaderField.frozen({
name: 'deviceMajorNumber',
offset: 329,
size: 8,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
UstarHeaderField.deviceMinorNumber = UstarHeaderField.frozen({
name: 'deviceMinorNumber',
offset: 337,
size: 8,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});
UstarHeaderField.fileNamePrefix = UstarHeaderField.frozen({
name: 'fileNamePrefix',
offset: 345,
size: 155,
type: UstarHeaderFieldType.ASCII_PADDED_END,
});