Repository navigation
fix(web): show TAR hard links in archive previews #34329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0567764
37d427a
f6ff3a8
7f9cdff
2075331
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,7 @@ function tarEntries(bytes: Uint8Array<ArrayBuffer>) { | |
| return Number.parseInt(value || "0", 8); | ||
| }; | ||
| let longPath: string | null = null; | ||
| let longLink: string | null = null; | ||
| const globalPax: Record<string, string> = {}; | ||
| let localPax: Record<string, string> = {}; | ||
| let offset = 0; | ||
|
|
@@ -64,7 +65,7 @@ function tarEntries(bytes: Uint8Array<ArrayBuffer>) { | |
| checksum += index >= 148 && index < 156 ? 32 : bytes[offset + index]!; | ||
| if (checksum !== number(offset + 148, 8)) throw new Error("The TAR header is damaged."); | ||
| const type = text(offset + 156, 1); | ||
| const metadata = type === "L" || type === "x" || type === "g"; | ||
| const metadata = type === "L" || type === "K" || type === "x" || type === "g"; | ||
| const attributes: Record<string, string> = metadata ? {} : { ...globalPax, ...localPax }; | ||
| const size = attributes.size ? Number(attributes.size) : number(offset + 124, 12); | ||
| if (attributes.size && !/^\d+$/.test(attributes.size)) | ||
|
|
@@ -75,7 +76,9 @@ function tarEntries(bytes: Uint8Array<ArrayBuffer>) { | |
| : ""; | ||
| const path = | ||
| attributes.path || longPath || [prefix, text(offset, 100)].filter(Boolean).join("/"); | ||
| const linkpath = attributes.linkpath ?? longLink ?? text(offset + 157, 100); | ||
| if (!metadata) { | ||
| longLink = null; | ||
| longPath = null; | ||
| localPax = {}; | ||
| } | ||
|
|
@@ -89,6 +92,7 @@ function tarEntries(bytes: Uint8Array<ArrayBuffer>) { | |
| throw new Error("An archive entry is too large or damaged."); | ||
| const content = bytes.subarray(offset + 512, offset + 512 + size); | ||
| if (type === "L") longPath = new TextDecoder().decode(content).replace(/\0.*$/s, ""); | ||
| else if (type === "K") longLink = new TextDecoder().decode(content).replace(/\0.*$/s, ""); | ||
| else if (type === "x" || type === "g") { | ||
| if (size > 65536) throw new Error("Archive metadata is too large."); | ||
| for (let cursor = 0; cursor < content.length;) { | ||
|
|
@@ -111,26 +115,38 @@ function tarEntries(bytes: Uint8Array<ArrayBuffer>) { | |
| if (equals < 1) throw new Error("The TAR metadata is damaged."); | ||
| const key = record.slice(0, equals); | ||
| const value = record.slice(equals + 1); | ||
| if (["path", "size", "mtime"].includes(key)) { | ||
| if (["path", "linkpath", "size", "mtime"].includes(key)) { | ||
| if (type === "x") localPax[key] = value; | ||
| else if (value) globalPax[key] = value; | ||
| else if (value || key === "linkpath") globalPax[key] = value; | ||
| else delete globalPax[key]; | ||
| } | ||
| cursor = end; | ||
| } | ||
| } else if (type === "" || type === "0" || type === "5") { | ||
| } else if ( | ||
| type === "" || | ||
| type === "0" || | ||
| type === "5" || | ||
| // A hard link shares an earlier member's bytes; one without that member is skipped. | ||
| (type === "1" && linkpath && files.has(linkpath)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolve equivalent archive-relative target names. If an earlier regular member is named Use consistent normalized keys for target resolution and replacement tracking. Keep displayed paths unchanged. Extend the existing real-worker fixture with this case.
|
||
| ) { | ||
| if (attributes.mtime && !/^[+-]?\d+(?:\.\d+)?$/.test(attributes.mtime)) | ||
| throw new Error("The TAR metadata is damaged."); | ||
| const modified = | ||
| (attributes.mtime ? Number(attributes.mtime) : number(offset + 136, 12)) * 1000; | ||
| const date = new Date(modified); | ||
| const linked = type === "1" ? files.get(linkpath) : undefined; | ||
| entries.push({ | ||
| path, | ||
| size, | ||
| size: linked?.length ?? size, | ||
|
voltcrash marked this conversation as resolved.
Resolved
|
||
| directory: type === "5", | ||
| modified: Number.isNaN(date.getTime()) ? null : date.toISOString(), | ||
| }); | ||
| if (type !== "5") files.set(path, content.slice()); | ||
| if (type === "5") { | ||
| files.delete(path); | ||
| files.delete(path.replace(/\/+$/, "")); | ||
| } else files.set(path, linked ?? content.slice()); | ||
| } else { | ||
| files.delete(path); | ||
| } | ||
| offset += 512 + Math.ceil(size / 512) * 512; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.