Skip to content

Commit

Permalink
feat: complete pax
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Aug 26, 2024
1 parent 9f131e5 commit 754fcf7
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/head.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable stylistic/indent */
/* eslint-disable no-labels */
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
// https://www.gnu.org/software/tar/manual/html_node/Portability.html#Portability
Expand Down Expand Up @@ -311,7 +312,13 @@ export function decode(b: Uint8Array, options?: DecodingHeadOptions) {
const size = decodeOctal(b, 124, 12)
const mtime = decodeOctal(b, 136, 12)
// convert as enum
let typeflag = b[156] === 0 ? TypeFlag.AREG_TYPE : (b[156] - 48) + '' as unknown as TypeFlag
let typeflag = b[156] === 0
? TypeFlag.AREG_TYPE
: b[156] === 120
? TypeFlag.XHD_TYPE
: b[156] === 103
? TypeFlag.XGL_TYPE
: (b[156] - 48) + '' as unknown as TypeFlag
const linkname = b[157] === Magic.NULL_CHAR ? null : decodeString(b, 157, 100, filenameEncoding)
const uname = decodeString(b, 265, 32)
const gname = decodeString(b, 297, 32)
Expand Down
57 changes: 45 additions & 12 deletions src/stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Readable, Writable } from 'stream'
import type { ReadableOptions, WritableOptions } from 'stream'
import { F_MODE, TypeFlag, decode, encode } from './head'
import { F_MODE, TypeFlag, decode, decodePax, encode, encodePax } from './head'
import type { DecodingHeadOptions, EncodingHeadOptions, EncodingHeadOptionsWithPax } from './head'
import { List, createList } from './list'
import { noop } from './shared'
Expand Down Expand Up @@ -45,14 +45,7 @@ export class Pack {
private resolveHeadOptions(size: number, options: PackOptions): EncodingHeadOptionsWithPax {
const { filename, ...rest } = options

const merged = { ...defaultPackOptions, ...rest, name: filename, mtime: Math.floor(Date.now() / 1000), size }

// Fallback to posix the global isn't support for now.
if (size.toString(8).length > 11) {
merged.typeflag = TypeFlag.XHD_TYPE
}

return merged
return { ...defaultPackOptions, ...rest, name: filename, mtime: Math.floor(Date.now() / 1000), size }
}

add(binary: Uint8Array, options: PackOptions) {
Expand All @@ -76,11 +69,23 @@ export class Pack {
}

private transport(binary: Uint8Array, resolvedOptions: EncodingHeadOptionsWithPax) {
const consume = (chunk: Uint8Array) => {
if (resolvedOptions.pax) {
const paxHead = encodePax({ name: resolvedOptions.name, linkname: resolvedOptions.linkname || '', pax: { ...resolvedOptions.pax } })
const head = encode({ ...resolvedOptions, name: 'PaxHeader', typeflag: TypeFlag.XHD_TYPE, size: paxHead.length })
this.reader.push(head)
this.reader.push(paxHead)
this.reader.push(this.fix(paxHead.length))
resolvedOptions.name = 'PaxHeader'
}
this.reader.push(encode(resolvedOptions))
this.reader.push(chunk)
}

const writer = createWriteableStream({
write: (chunk, _, callback) => {
try {
this.reader.push(encode(resolvedOptions))
this.reader.push(chunk)
consume(chunk)
callback()
} catch (error) {
callback(error as Error)
Expand Down Expand Up @@ -163,6 +168,8 @@ export class Extract {
private flag: boolean
private elt: Uint8Array | null
private total: number
private isPax: boolean
private paxMeta: Record<string, string>
constructor(options: DecodingHeadOptions) {
this.decodeOptions = options
this.matrix = new FastBytes()
Expand All @@ -172,6 +179,8 @@ export class Extract {
this.offset = 0
this.elt = null
this.total = 0
this.isPax = false
this.paxMeta = Object.create(null)
this.writer = createWriteableStream({
write: (chunk, _, callback) => {
this.matrix.push(chunk)
Expand All @@ -195,6 +204,18 @@ export class Extract {
const decodeHead = () => {
try {
this.head = decode(this.matrix.shift(512), this.decodeOptions)
if (this.head.typeflag === TypeFlag.XHD_TYPE) {
this.isPax = true
return true
}
if (Object.keys(this.paxMeta).length > 0) {
this.head.name = this.paxMeta.path
this.head.linkname = this.paxMeta.linkpath
// @ts-expect-error
this.head.pax = { ...this.paxMeta }
this.paxMeta = Object.create(null)
}

this.missing = this.head.size
this.elt = new Uint8Array(this.head.size)
this.flag = true
Expand All @@ -216,13 +237,25 @@ export class Extract {
return
}
this.elt!.set(this.matrix.shift(this.missing), this.offset)

this.total += this.elt!.length + 512
this.writer.emit('entry', this.head, this.elt!)
this.flag = false
}

const handlePax = () => {
const c = this.matrix.shift(this.head.size)
const paxHead = decodePax(c)
this.paxMeta = { ...this.paxMeta, ...paxHead }
this.total += this.head.size + 512
this.isPax = false
}

while (this.matrix.bytesLen > 0) {
if (this.isPax) {
handlePax()
continue
}

if (this.flag) {
consume()
continue
Expand Down

0 comments on commit 754fcf7

Please sign in to comment.