-
-
Save trentm/6ec490d23938b81e04d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/img/lib/common.js b/src/img/lib/common.js | |
index 6962fc3..98c92f0 100644 | |
--- a/src/img/lib/common.js | |
+++ b/src/img/lib/common.js | |
@@ -300,6 +300,47 @@ function execFilePlus(args, cb) { | |
/** | |
+ * A convenience wrapper around `child_process.exec` to take away some | |
+ * logging and error handling boilerplate. | |
+ * | |
+ * @param args {Object} | |
+ * - command {String} Required. | |
+ * - execOpts {Array} Exec options. | |
+ * - log {Bunyan Logger} Required. Use to log details at trace level. | |
+ * @param cb {Function} `function (err, stdout, stderr)` where `err` here is | |
+ * an `errors.InternalError` wrapper around the child_process error. | |
+ */ | |
+function execPlus(args, cb) { | |
+ assert.object(args, 'args'); | |
+ assert.string(args.command, 'args.command'); | |
+ assert.optionalObject(args.execOpts, 'args.execOpts'); | |
+ assert.object(args.log, 'args.log'); | |
+ assert.func(cb); | |
+ var command = args.command; | |
+ var execOpts = args.execOpts; | |
+ | |
+ // args.log.trace({exec: true, command: command, execOpts: execOpts}, | |
+ // 'exec start'); | |
+ execFile(command, execOpts, function (err, stdout, stderr) { | |
+ args.log.trace({exec: true, command: command, execOpts: execOpts, | |
+ err: err, stdout: stdout, stderr: stderr}, 'exec done'); | |
+ if (err) { | |
+ var msg = format( | |
+ 'exec error:\n' | |
+ + '\tcommand: %s\n' | |
+ + '\texit status: %s\n' | |
+ + '\tstdout:\n%s\n' | |
+ + '\tstderr:\n%s', | |
+ command, err.code, stdout.trim(), stderr.trim()); | |
+ cb(new InternalError({cause: err, message: msg}), stdout, stderr); | |
+ } else { | |
+ cb(null, stdout, stderr); | |
+ } | |
+ }); | |
+} | |
+ | |
+ | |
+/** | |
* Call `vmadm stop UUID`. | |
* | |
* @param uuid {String} The current snapshot name. | |
@@ -687,6 +728,7 @@ module.exports = { | |
humanSizeFromBytes: humanSizeFromBytes, | |
normUrlFromUrl: normUrlFromUrl, | |
execFilePlus: execFilePlus, | |
+ execPlus: execPlus, | |
vmStop: vmStop, | |
vmStart: vmStart, | |
diff --git a/src/img/lib/imgadm.js b/src/img/lib/imgadm.js | |
index 90d07c5..a8352ed 100644 | |
--- a/src/img/lib/imgadm.js | |
+++ b/src/img/lib/imgadm.js | |
@@ -72,6 +72,7 @@ var common = require('./common'), | |
var configuration = require('./configuration'); | |
var Database = require('./database'); | |
var errors = require('./errors'); | |
+var magic = require('./magic'); | |
var mod_sources = require('./sources'); | |
var upgrade = require('./upgrade'); | |
@@ -1711,8 +1712,9 @@ IMGADM.prototype._importImage = function _importImage(opts, cb) { | |
var totalBytes = irecs | |
.map(function (irec) { return irec.imgMeta.size; }) | |
.reduce(function (a, b) { return a + b; }); | |
- logCb('Must download and install %d images (%s)', | |
- irecs.length, common.humanSizeFromBytes(totalBytes)); | |
+ logCb('Must download and install %d image%s (%s)', | |
+ irecs.length, (irecs.length === 1 ? '' : 's'), | |
+ common.humanSizeFromBytes(totalBytes)); | |
if (!opts.quiet && process.stderr.isTTY) { | |
bar = new ProgressBar({ | |
size: totalBytes, | |
@@ -2092,13 +2094,49 @@ IMGADM.prototype._installDockerImage = function _installDockerImage(ctx, cb) { | |
]}, next); | |
}, | |
+ function sniffCompression(_, next) { | |
+ assert.string(ctx.filePath, 'ctx.filePath'); | |
+ magic.compressionTypeFromPath(ctx.filePath, function (err, cType) { | |
+ if (err) { | |
+ next(err); | |
+ return; | |
+ } | |
+ ctx.cType = cType; // one of: null, bzip2, gzip, xz | |
+ next(); | |
+ }); | |
+ }, | |
+ | |
// Dev Note: If we can just gtar a file and NOT do streaming into | |
// gtar, then it'll handle compression detection for us, in particular | |
// for docker files for which we don't track compression. | |
function extract(_, next) { | |
assert.string(ctx.filePath, 'ctx.filePath'); | |
- var argv = ['/usr/bin/gtar', '-xf', ctx.filePath, '-C', zoneroot]; | |
- execFilePlus({argv: argv, log: log}, next); | |
+ assert.optionalString(ctx.cType, 'ctx.cType'); | |
+ | |
+ if (ctx.cType === 'xz') { | |
+ common.execPlus({ | |
+ command: format( | |
+ '/usr/bin/xz -dc %s | /usr/bin/gtar -x -C %s', | |
+ ctx.filePath, zoneroot), | |
+ log: log, | |
+ execOpts: { | |
+ maxBuffer: 2 * 1024 * 1024 | |
+ } | |
+ }, next); | |
+ } else { | |
+ var tarOpt = { | |
+ bzip2: 'j', | |
+ gzip: 'z' | |
+ }[ctx.cType] || ''; | |
+ common.execFilePlus({ | |
+ argv: ['/usr/bin/gtar', '-x' + tarOpt + 'f', | |
+ ctx.filePath, '-C', zoneroot], | |
+ log: log, | |
+ execOpts: { | |
+ maxBuffer: 2 * 1024 * 1024 | |
+ } | |
+ }, next); | |
+ } | |
}, | |
function whiteout(_, next) { | |
diff --git a/src/img/lib/magic.js b/src/img/lib/magic.js | |
new file mode 100644 | |
index 0000000..9af4c9c | |
--- /dev/null | |
+++ b/src/img/lib/magic.js | |
@@ -0,0 +1,104 @@ | |
+/* | |
+ * CDDL HEADER START | |
+ * | |
+ * The contents of this file are subject to the terms of the | |
+ * Common Development and Distribution License, Version 1.0 only | |
+ * (the "License"). You may not use this file except in compliance | |
+ * with the License. | |
+ * | |
+ * You can obtain a copy of the license at http://smartos.org/CDDL | |
+ * | |
+ * See the License for the specific language governing permissions | |
+ * and limitations under the License. | |
+ * | |
+ * When distributing Covered Code, include this CDDL HEADER in each | |
+ * file. | |
+ * | |
+ * If applicable, add the following below this CDDL HEADER, with the | |
+ * fields enclosed by brackets "[]" replaced with your own identifying | |
+ * information: Portions Copyright [yyyy] [name of copyright owner] | |
+ * | |
+ * CDDL HEADER END | |
+ * | |
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved. | |
+ * | |
+ * * * | |
+ * Magic number sniffing of compression type of files. | |
+ */ | |
+ | |
+var p = console.log; | |
+var assert = require('assert-plus'); | |
+var format = require('util').format; | |
+var fs = require('fs'); | |
+var vasync = require('vasync'); | |
+ | |
+ | |
+ | |
+// ---- compression type sniffing | |
+ | |
+var magicNumbers = { | |
+ // <compression type> : <magic number> | |
+ bzip2: new Buffer([0x42, 0x5A, 0x68]), | |
+ gzip: new Buffer([0x1F, 0x8B, 0x08]), | |
+ xz: new Buffer([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]) | |
+}; | |
+var maxMagicLen = 0; | |
+Object.keys(magicNumbers).forEach(function (type) { | |
+ maxMagicLen = Math.max(maxMagicLen, magicNumbers[type].length); | |
+}); | |
+ | |
+function bufNEquals(a, b, n) { | |
+ assert.ok(a.length >= n, format( | |
+ 'buffer "a" length (%d) is shorter than "n" (%d)', a.length, n)); | |
+ assert.ok(b.length >= n, format( | |
+ 'buffer "b" length (%d) is shorter than "n" (%d)', b.length, n)); | |
+ | |
+ for (var i = 0; i < n; i++) { | |
+ if (a[i] !== b[i]) { | |
+ return false; | |
+ } | |
+ } | |
+ return true; | |
+} | |
+ | |
+function compressionTypeFromBufSync(buf) { | |
+ var types = Object.keys(magicNumbers); | |
+ for (var i = 0; i < types.length; i++) { | |
+ var type = types[i]; | |
+ var magic = magicNumbers[type]; | |
+ if (bufNEquals(buf, magic, magic.length)) { | |
+ return type; | |
+ } | |
+ } | |
+ return null; | |
+} | |
+ | |
+function compressionTypeFromPath(path, cb) { | |
+ fs.open(path, 'r', function (oErr, fd) { | |
+ if (oErr) { | |
+ cb(oErr); | |
+ return; | |
+ } | |
+ var buf = new Buffer(maxMagicLen); | |
+ fs.read(fd, buf, 0, buf.length, 0, function (rErr, bytesRead, buffer) { | |
+ if (rErr) { | |
+ cb(rErr); | |
+ return; | |
+ } | |
+ fs.close(fd, function (cErr) { | |
+ if (cErr) { | |
+ cb(cErr); | |
+ return; | |
+ } | |
+ cb(null, compressionTypeFromBufSync(buf)); | |
+ }); | |
+ }); | |
+ }); | |
+} | |
+ | |
+ | |
+// ---- exports | |
+ | |
+module.exports = { | |
+ compressionTypeFromPath: compressionTypeFromPath | |
+}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment