simplify more
This commit is contained in:
parent
09749812bf
commit
ca345d9b06
|
|
@ -14,8 +14,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { Writable } from 'stream';
|
import { Writable, once } from 'stream';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
enum TarType {
|
||||||
|
REGTYPE,
|
||||||
|
LNKTYPE,
|
||||||
|
SYMTYPE,
|
||||||
|
CHRTYPE,
|
||||||
|
BLKTYPE,
|
||||||
|
DIRTYPE,
|
||||||
|
FIFOTYPE,
|
||||||
|
CONTTYPE
|
||||||
|
}
|
||||||
|
|
||||||
function parseHeader(buffer: Buffer) {
|
function parseHeader(buffer: Buffer) {
|
||||||
if (buffer.length < 512)
|
if (buffer.length < 512)
|
||||||
|
|
@ -25,29 +37,21 @@ function parseHeader(buffer: Buffer) {
|
||||||
const prefixField = buffer.toString('utf8', 345, 500).replace(/\0/g, '');
|
const prefixField = buffer.toString('utf8', 345, 500).replace(/\0/g, '');
|
||||||
if (prefixField)
|
if (prefixField)
|
||||||
name = path.join(prefixField, name);
|
name = path.join(prefixField, name);
|
||||||
|
name = name.replace(/^\/+/, '');
|
||||||
|
|
||||||
const size = parseInt(buffer.toString('utf8', 124, 136).trim(), 8);
|
const size = parseInt(buffer.toString('utf8', 124, 136).trim(), 8);
|
||||||
const typeFlag = buffer[156];
|
const type = parseInt(buffer.toString('ascii', 156, 157), 10) as TarType;
|
||||||
const mode = parseInt(buffer.toString('utf8', 100, 108).trim(), 8);
|
const mode = parseInt(buffer.toString('utf8', 100, 108).trim(), 8) || 0o644;
|
||||||
const linkname = buffer.toString('utf8', 157, 257).replace(/\0/g, '');
|
const linkname = buffer.toString('utf8', 157, 257).replace(/\0/g, '');
|
||||||
|
|
||||||
// Parse user and group IDs
|
|
||||||
const uid = parseInt(buffer.toString('utf8', 108, 116).trim(), 8);
|
const uid = parseInt(buffer.toString('utf8', 108, 116).trim(), 8);
|
||||||
const gid = parseInt(buffer.toString('utf8', 116, 124).trim(), 8);
|
const gid = parseInt(buffer.toString('utf8', 116, 124).trim(), 8);
|
||||||
|
|
||||||
let type = 'file';
|
|
||||||
if (typeFlag === 53) // ASCII '5'
|
|
||||||
type = 'directory';
|
|
||||||
else if (typeFlag === 50) // ASCII '2'
|
|
||||||
type = 'symlink';
|
|
||||||
else if (typeFlag === 0 || typeFlag === 48) // ASCII '0'
|
|
||||||
type = 'file';
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name.replace(/^\/+/, ''),
|
name,
|
||||||
size,
|
size,
|
||||||
type,
|
type,
|
||||||
mode: mode || 0o644,
|
mode,
|
||||||
linkname,
|
linkname,
|
||||||
uid,
|
uid,
|
||||||
gid
|
gid
|
||||||
|
|
@ -81,48 +85,49 @@ export class TarExtractor extends Writable {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentHeader = parseHeader(this.buffer);
|
const header = parseHeader(this.buffer);
|
||||||
this.buffer = this.buffer.subarray(512);
|
this.buffer = this.buffer.subarray(512);
|
||||||
await this.processHeader();
|
|
||||||
|
|
||||||
if (!this.currentFileStream)
|
const fullPath = this.outputPath(header.name);
|
||||||
this.currentHeader = null;
|
switch (header.type) {
|
||||||
|
case TarType.DIRTYPE:
|
||||||
|
await fs.promises.mkdir(fullPath, { recursive: true, mode: 0o755 });
|
||||||
|
break;
|
||||||
|
case TarType.SYMTYPE:
|
||||||
|
await fs.promises.symlink(header.linkname, fullPath);
|
||||||
|
break;
|
||||||
|
case TarType.REGTYPE:
|
||||||
|
this.currentFileStream = fs.createWriteStream(fullPath, { mode: header.mode });
|
||||||
|
await once(this.currentFileStream, 'ready');
|
||||||
|
this.currentHeader = header;
|
||||||
|
this.remainingBytes = header.size;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported type ${header.type} for '${header.name}'`);
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.remainingBytes > 0) {
|
assert(this.currentFileStream);
|
||||||
const dataChunk = this.buffer.subarray(0, this.remainingBytes);
|
assert(this.remainingBytes > 0);
|
||||||
this.buffer = this.buffer.subarray(this.remainingBytes);
|
|
||||||
this.remainingBytes -= dataChunk.length;
|
|
||||||
|
|
||||||
this.currentFileStream!.write(dataChunk);
|
const dataChunk = this.buffer.subarray(0, this.remainingBytes);
|
||||||
|
this.buffer = this.buffer.subarray(this.remainingBytes);
|
||||||
|
this.remainingBytes -= dataChunk.length;
|
||||||
|
|
||||||
if (this.remainingBytes === 0) {
|
this.currentFileStream.write(dataChunk);
|
||||||
const padding = 512 - (this.currentHeader!.size % 512);
|
|
||||||
if (padding < 512)
|
|
||||||
this.buffer = this.buffer.subarray(padding);
|
|
||||||
|
|
||||||
this.currentFileStream!.end();
|
if (this.remainingBytes === 0) {
|
||||||
this.currentHeader = null;
|
this.currentFileStream.end();
|
||||||
this.currentFileStream = null;
|
this.currentFileStream = null;
|
||||||
}
|
|
||||||
|
const padding = 512 - (this.currentHeader.size % 512);
|
||||||
|
if (padding < 512)
|
||||||
|
this.buffer = this.buffer.subarray(padding);
|
||||||
|
|
||||||
|
this.currentHeader = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processHeader() {
|
|
||||||
if (!this.currentHeader)
|
|
||||||
throw new Error('No header');
|
|
||||||
const fullPath = this.outputPath(this.currentHeader.name);
|
|
||||||
|
|
||||||
if (this.currentHeader.type === 'directory') {
|
|
||||||
await fs.promises.mkdir(fullPath, { recursive: true, mode: 0o755 });
|
|
||||||
} else if (this.currentHeader.type === 'symlink') {
|
|
||||||
await fs.promises.symlink(this.currentHeader.linkname, fullPath);
|
|
||||||
} else {
|
|
||||||
this.currentFileStream = fs.createWriteStream(fullPath, { mode: this.currentHeader.mode });
|
|
||||||
this.remainingBytes = this.currentHeader.size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue