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:
Nicolas Raynaud
2019-02-07 14:51:38 -07:00
committed by Julien Fontanet
parent e484b073e1
commit 0a63acac73
3 changed files with 50 additions and 49 deletions

View File

@@ -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))

View File

@@ -231,6 +231,7 @@ const parseFile = async (file, type, func) => {
type,
}
} catch (error) {
console.error(error)
return { error, file, type }
}
}

View File

@@ -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)