fix(OVA import): support big files (#3504)

Fixes #3468

- fix bug in VMDK header parsing
- parse extended tar headers
This commit is contained in:
Nicolas Raynaud 2019-01-16 02:31:52 -07:00 committed by Julien Fontanet
parent cfd956631b
commit df809baaaf
3 changed files with 46 additions and 13 deletions

View File

@ -11,9 +11,11 @@
- [New SR] No redirection if the SR creation failed or canceled [#3843](https://github.com/vatesfr/xen-orchestra/issues/3843) (PR [#3853](https://github.com/vatesfr/xen-orchestra/pull/3853)) - [New SR] No redirection if the SR creation failed or canceled [#3843](https://github.com/vatesfr/xen-orchestra/issues/3843) (PR [#3853](https://github.com/vatesfr/xen-orchestra/pull/3853))
- [Home] Fix two tabs opened by middle click in Firefox [#3450](https://github.com/vatesfr/xen-orchestra/issues/3450) (PR [#3825](https://github.com/vatesfr/xen-orchestra/pull/3825)) - [Home] Fix two tabs opened by middle click in Firefox [#3450](https://github.com/vatesfr/xen-orchestra/issues/3450) (PR [#3825](https://github.com/vatesfr/xen-orchestra/pull/3825))
- [XOA] Enable downgrade for ending trial (PR [#3867](https://github.com/vatesfr/xen-orchestra/pull/3867)) - [XOA] Enable downgrade for ending trial (PR [#3867](https://github.com/vatesfr/xen-orchestra/pull/3867))
- [OVA import] allow import of big files [#3468](https://github.com/vatesfr/xen-orchestra/issues/3468) (PR [#3504](https://github.com/vatesfr/xen-orchestra/pull/3504))
### Released packages ### Released packages
- xo-vmdk-to-vhd v0.1.6
- xo-server v5.34.0 - xo-server v5.34.0
- xo-web v5.34.0 - xo-web v5.34.0

View File

@ -54,10 +54,9 @@ export default async function readVmdkGrainTable(fileAccessor) {
const grainSize = const grainSize =
getLongLong(headerBuffer, GRAIN_SIZE_OFFSET, 'grain size') * SECTOR_SIZE getLongLong(headerBuffer, GRAIN_SIZE_OFFSET, 'grain size') * SECTOR_SIZE
const grainCount = Math.ceil(capacity / grainSize) const grainCount = Math.ceil(capacity / grainSize)
const numGTEsPerGT = getLongLong( const numGTEsPerGT = new DataView(headerBuffer).getUint32(
headerBuffer,
NUM_GTE_PER_GT_OFFSET, NUM_GTE_PER_GT_OFFSET,
'num GTE per GT' true
) )
const grainTablePhysicalSize = numGTEsPerGT * 4 const grainTablePhysicalSize = numGTEsPerGT * 4
const grainDirectoryEntries = Math.ceil(grainCount / numGTEsPerGT) const grainDirectoryEntries = Math.ceil(grainCount / numGTEsPerGT)

View File

@ -102,7 +102,10 @@ function parseTarHeader(header) {
Buffer.from(header.slice(124, 124 + 11)).toString('ascii'), Buffer.from(header.slice(124, 124 + 11)).toString('ascii'),
8 8
) )
return { fileName, fileSize } // 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]
const fileType = typeSlice === 0 ? '0' : String.fromCharCode(typeSlice)
return { fileName, fileSize, fileType }
} }
async function parseOVF(fileFragment) { async function parseOVF(fileFragment) {
@ -172,27 +175,56 @@ async function parseOVF(fileFragment) {
) )
} }
// tar spec: https://www.gnu.org/software/tar/manual/html_node/Standard.html
async function parseTarFile(file) { async function parseTarFile(file) {
let offset = 0 let offset = 0
const HEADER_SIZE = 512 const HEADER_SIZE = 512
let data = { tables: {} } let data = { tables: {} }
while (offset + HEADER_SIZE <= file.size) { while (offset + HEADER_SIZE <= file.size) {
const header = parseTarHeader( let header = parseTarHeader(
await readFileFragment(file, offset, offset + HEADER_SIZE) await readFileFragment(file, offset, offset + HEADER_SIZE)
) )
offset += HEADER_SIZE offset += HEADER_SIZE
if (header === null) { if (header === null) {
break break
} }
if (header.fileName.toLowerCase().endsWith('.ovf')) { // extended header: it's a text file named 'PaxHeader/<filename.ext>' appearing before the file.
const res = await parseOVF(file.slice(offset, offset + header.fileSize)) // the attribute are ascii lines of form: "charcount key=value\n". Charcount is the number of chars in the line.
data = { ...data, ...res } // 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(
await readFileFragment(file, offset, offset + HEADER_SIZE)
)
offset += HEADER_SIZE
if (foundSize) {
header.fileSize = parseInt(foundSize.split('=')[1])
}
} }
if (header.fileName.toLowerCase().endsWith('.vmdk')) { // remove mac os X forks https://stackoverflow.com/questions/8766730/tar-command-in-mac-os-x-adding-hidden-files-why
const fileSlice = file.slice(offset, offset + header.fileSize) if (
const readFile = async (start, end) => header.fileType === '0' &&
readFileFragment(fileSlice, start, end) !header.fileName.toLowerCase().startsWith('./._')
data.tables[header.fileName] = await readVmdkGrainTable(readFile) ) {
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 offset += Math.ceil(header.fileSize / 512) * 512
} }