Skip to content

Commit

Permalink
feat(parse): handle long file names (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Jan 21, 2025
1 parent afa131b commit 0cbd1e8
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 26 deletions.
59 changes: 40 additions & 19 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,19 @@ export function parseTar<

while (offset < buffer.byteLength - 512) {
// File name (offset: 0 - length: 100)
const name = _readString(buffer, offset, 100);
let name = _readString(buffer, offset, 100);
if (name.length === 0) {
break;
}

// Long file-name handling
if (nextExtendedHeader) {
const longName = nextExtendedHeader.path || nextExtendedHeader.linkpath;
if (longName) {
name = longName;
}
}

// File mode (offset: 100 - length: 8)
const mode = _readString(buffer, offset + 100, 8).trim();

Expand All @@ -62,25 +70,38 @@ export function parseTar<

// File type (offset: 156 - length: 1)
// prettier-ignore
const _type = _readString(buffer, offset + 156, 1) as keyof typeof tarItemTypeMap;
const type = tarItemTypeMap[_type];

// Extended headers
if (type === "extendedHeader" || type === "globalExtendedHeader") {
const headers = _parseExtendedHeaders(
new Uint8Array(buffer, offset + 512, size),
);
if (type === "extendedHeader") {
nextExtendedHeader = headers;
} else {
nextExtendedHeader = undefined;
globalExtendedHeader = {
...globalExtendedHeader,
...headers,
};
const _type = (_readString(buffer, offset + 156, 1) || "0") as keyof typeof tarItemTypeMap;
const type = tarItemTypeMap[_type] || _type;

// Special types
switch (type) {
// Extended headers for next entry
case "extendedHeader" /* x */:
case "globalExtendedHeader" /* g */: {
const headers = _parseExtendedHeaders(
new Uint8Array(buffer, offset + 512, size),
);
if (type === "extendedHeader") {
nextExtendedHeader = headers;
} else {
nextExtendedHeader = undefined;
globalExtendedHeader = {
...globalExtendedHeader,
...headers,
};
}
offset += seek;
continue;
}
offset += seek;
continue;
// GNU tar long file names
case "gnuLongFileName" /* L */:
case "gnuOldLongFileName" /* N */:
case "gnuLongLinkName" /* K */: {
nextExtendedHeader = { path: _readString(buffer, offset + 512, size) };
offset += seek;
continue;
}
// No default
}

// Ustar indicator (offset: 257 - length: 6)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
long file
Binary file modified test/fixtures/out/gnu.tar
Binary file not shown.
Binary file modified test/fixtures/out/pax.tar
Binary file not shown.
Binary file modified test/fixtures/out/ustar.tar
Binary file not shown.
Binary file modified test/fixtures/out/v7.tar
Binary file not shown.
24 changes: 17 additions & 7 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,23 @@ describe("parse", () => {
new URL(`fixtures/out/${format}.tar`, import.meta.url),
);
const parsed = await parseTar(blob);
const names = parsed.map((f) => f.name);
expect(names).toMatchObject([
"./",
"./foo.txt",
"./bar/",
"./bar/baz.txt",
]);

const expectedFiles = ["./foo.txt", "./bar/baz.txt"];

// Long filenames
if (!["v7", "ustar"].includes(format)) {
expectedFiles.push(
`./long/[160]#${"-".repeat(153)}#/file.txt`,
`./long/[160]#${"-".repeat(153)}#/link`,
);
}

expect(
parsed
.filter((i) => i.type !== "directory")
.map((i) => i.name)
.sort(),
).toMatchObject(expectedFiles.sort());
});
}
});
Expand Down

0 comments on commit 0cbd1e8

Please sign in to comment.