fix(OVA import): fix tar file size parsing (#3941)
Avoids relying on PAX header, uses a weird encoding in the normal filesize header. Fixes #3900
This commit is contained in:
committed by
Julien Fontanet
parent
e484b073e1
commit
0a63acac73
@@ -9,6 +9,7 @@
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Import] Fix import of big OVA files
|
||||
- [Host] Show the host's memory usage instead of the sum of the VMs' memory usage (PR [#3924](https://github.com/vatesfr/xen-orchestra/pull/3924))
|
||||
- [SAML] Make `AssertionConsumerServiceURL` matches the callback URL
|
||||
- [Backup NG] Correctly delete broken VHD chains [#3875](https://github.com/vatesfr/xen-orchestra/issues/3875) (PR [#3939](https://github.com/vatesfr/xen-orchestra/pull/3939))
|
||||
|
||||
@@ -231,6 +231,7 @@ const parseFile = async (file, type, func) => {
|
||||
type,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return { error, file, type }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +98,23 @@ function parseTarHeader(header) {
|
||||
if (fileName.length === 0) {
|
||||
return null
|
||||
}
|
||||
const fileSize = parseInt(
|
||||
Buffer.from(header.slice(124, 124 + 11)).toString('ascii'),
|
||||
8
|
||||
// https://stackoverflow.com/a/2511526/72637
|
||||
const sizeBuffer = Buffer.from(header.slice(124, 124 + 12))
|
||||
let fileSize = 0
|
||||
if (sizeBuffer[0] === 0x80) {
|
||||
// https://github.com/chrisdickinson/tar-parse/blob/master/header.js#L271
|
||||
// remove the head byte and go in decreasing power order.
|
||||
for (let i = 1; i < sizeBuffer.length; i++) {
|
||||
fileSize = fileSize * 256 + sizeBuffer[i]
|
||||
}
|
||||
} else fileSize = parseInt(sizeBuffer.slice(0, -1).toString('ascii'), 8)
|
||||
console.log(
|
||||
'fileSize',
|
||||
fileName,
|
||||
fileSize,
|
||||
'B',
|
||||
fileSize / Math.pow(1024, 3),
|
||||
'GB'
|
||||
)
|
||||
// normal files are either the char '0' (charcode 48) or the char null (charcode zero)
|
||||
const typeSlice = new Uint8Array(header.slice(156, 156 + 1))[0]
|
||||
@@ -177,58 +191,43 @@ async function parseOVF(fileFragment) {
|
||||
|
||||
// tar spec: https://www.gnu.org/software/tar/manual/html_node/Standard.html
|
||||
async function parseTarFile(file) {
|
||||
let offset = 0
|
||||
const HEADER_SIZE = 512
|
||||
let data = { tables: {} }
|
||||
while (offset + HEADER_SIZE <= file.size) {
|
||||
let header = parseTarHeader(
|
||||
await readFileFragment(file, offset, offset + HEADER_SIZE)
|
||||
)
|
||||
offset += HEADER_SIZE
|
||||
if (header === null) {
|
||||
break
|
||||
}
|
||||
// extended header: it's a text file named 'PaxHeader/<filename.ext>' appearing before the file.
|
||||
// the attribute are ascii lines of form: "charcount key=value\n". Charcount is the number of chars in the line.
|
||||
// Parsing it is the only way to get the size of big files because normal headers store the size in a
|
||||
// 12 char octal string, they can't store big sizes.
|
||||
if (header.fileType === 'x') {
|
||||
const paxFile = Buffer.from(
|
||||
await readFileFragment(file, offset, offset + header.fileSize)
|
||||
).toString()
|
||||
console.log('pax header', paxFile)
|
||||
const lines = paxFile.split('\n')
|
||||
// "<charcount> key=value\n"
|
||||
const foundSize = lines.find(l => l.match(/^[0-9]+ size=/))
|
||||
// go to next header
|
||||
offset += Math.ceil(header.fileSize / 512) * 512
|
||||
header = parseTarHeader(
|
||||
document.body.style.cursor = 'wait'
|
||||
try {
|
||||
let offset = 0
|
||||
const HEADER_SIZE = 512
|
||||
let data = { tables: {} }
|
||||
while (offset + HEADER_SIZE <= file.size) {
|
||||
const header = parseTarHeader(
|
||||
await readFileFragment(file, offset, offset + HEADER_SIZE)
|
||||
)
|
||||
offset += HEADER_SIZE
|
||||
if (foundSize) {
|
||||
header.fileSize = parseInt(foundSize.split('=')[1])
|
||||
if (header === null) {
|
||||
break
|
||||
}
|
||||
// remove mac os X forks https://stackoverflow.com/questions/8766730/tar-command-in-mac-os-x-adding-hidden-files-why
|
||||
if (
|
||||
header.fileType === '0' &&
|
||||
!header.fileName.toLowerCase().startsWith('./._')
|
||||
) {
|
||||
if (header.fileName.toLowerCase().endsWith('.ovf')) {
|
||||
const res = await parseOVF(
|
||||
file.slice(offset, offset + header.fileSize)
|
||||
)
|
||||
data = { ...data, ...res }
|
||||
}
|
||||
if (header.fileName.toLowerCase().endsWith('.vmdk')) {
|
||||
const fileSlice = file.slice(offset, offset + header.fileSize)
|
||||
const readFile = async (start, end) =>
|
||||
readFileFragment(fileSlice, start, end)
|
||||
data.tables[header.fileName] = await readVmdkGrainTable(readFile)
|
||||
}
|
||||
}
|
||||
offset += Math.ceil(header.fileSize / 512) * 512
|
||||
}
|
||||
// remove mac os X forks https://stackoverflow.com/questions/8766730/tar-command-in-mac-os-x-adding-hidden-files-why
|
||||
if (
|
||||
header.fileType === '0' &&
|
||||
!header.fileName.toLowerCase().startsWith('./._')
|
||||
) {
|
||||
if (header.fileName.toLowerCase().endsWith('.ovf')) {
|
||||
const res = await parseOVF(file.slice(offset, offset + header.fileSize))
|
||||
data = { ...data, ...res }
|
||||
}
|
||||
if (header.fileName.toLowerCase().endsWith('.vmdk')) {
|
||||
const fileSlice = file.slice(offset, offset + header.fileSize)
|
||||
const readFile = async (start, end) =>
|
||||
readFileFragment(fileSlice, start, end)
|
||||
data.tables[header.fileName] = await readVmdkGrainTable(readFile)
|
||||
}
|
||||
}
|
||||
offset += Math.ceil(header.fileSize / 512) * 512
|
||||
return data
|
||||
} finally {
|
||||
document.body.style.cursor = null
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const parseOvaFile = async file => parseTarFile(file)
|
||||
|
||||
Reference in New Issue
Block a user