mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 15:27:17 +00:00
fix(image-stream): fix Apple disk image detection & reading (#1283)
This fixes two things: The format detection, and a bug in `udif`. First, by categorizing the `.dmg` extension as compressed image, `.isSupportedImage()` would attempt to detect the format after stripping the extension, causing it to be misdetected. Second, `udif`'s ReadStream didn't add the `dataForkOffset` to its position when reading blocks, causing the wrong data to be read for some images, in turn causing zlib to error on invalid headers. Changes: - Classify `.dmg` as `type: 'image'` - Update `udif` to 0.8.0 Change-Type: patch Changelog-Entry: Fix Apple disk image detection & streaming Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
parent
847b41f49a
commit
6c930e2d8d
@ -58,4 +58,19 @@ These are the rules for handling archive images:
|
||||
|
||||
The module throws an error if the above rules are not met.
|
||||
|
||||
Supported Formats
|
||||
-----------------
|
||||
|
||||
There are currently three image types in supported formats: `image`, `compressed` and `archive`.
|
||||
|
||||
An extension tagged `image` describes a format which can be directly written to a device by its handler,
|
||||
and an extension tagged `archive` denotes an archive containing an image, and will cause an archive handler
|
||||
to open the archive and search for an image file.
|
||||
|
||||
Note that when marking an extension as `compressed`, the filename will be stripped of that extension,
|
||||
and the leftover extension examined to determine the uncompressed image format (i.e. `.img.gz -> .img`).
|
||||
|
||||
As an archive (such as `.tar`) might be additionally compressed, this will allow for constructs such as
|
||||
`.tar.gz` (a compressed archive, containing a file with an extension tagged as `image`) to be handled correctly.
|
||||
|
||||
[etcher-image-write]: https://github.com/resin-io-modules/etcher-image-write
|
||||
|
@ -16,6 +16,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @summary Supported filename extensions
|
||||
* @description
|
||||
* NOTE: Extensions with type: 'compressed' will be stripped
|
||||
* from filenames to determine the format of the uncompressed image.
|
||||
* For details, see lib/image-stream/README.md
|
||||
* @const {Array}
|
||||
*/
|
||||
module.exports = [
|
||||
{
|
||||
extension: 'zip',
|
||||
@ -37,10 +45,6 @@ module.exports = [
|
||||
extension: 'xz',
|
||||
type: 'compressed'
|
||||
},
|
||||
{
|
||||
extension: 'dmg',
|
||||
type: 'compressed'
|
||||
},
|
||||
{
|
||||
extension: 'img',
|
||||
type: 'image'
|
||||
@ -60,5 +64,9 @@ module.exports = [
|
||||
{
|
||||
extension: 'raw',
|
||||
type: 'image'
|
||||
},
|
||||
{
|
||||
extension: 'dmg',
|
||||
type: 'image'
|
||||
}
|
||||
];
|
||||
|
4
npm-shrinkwrap.json
generated
4
npm-shrinkwrap.json
generated
@ -6772,9 +6772,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"udif": {
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"from": "udif@latest",
|
||||
"resolved": "https://registry.npmjs.org/udif/-/udif-0.7.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/udif/-/udif-0.8.0.tgz",
|
||||
"dependencies": {
|
||||
"base64-js": {
|
||||
"version": "1.1.2",
|
||||
|
@ -26,7 +26,7 @@ describe('Shared: SupportedFormats', function() {
|
||||
|
||||
it('should return the supported compressed extensions', function() {
|
||||
const extensions = supportedFormats.getCompressedExtensions();
|
||||
m.chai.expect(extensions).to.deep.equal([ 'gz', 'bz2', 'xz', 'dmg' ]);
|
||||
m.chai.expect(extensions).to.deep.equal([ 'gz', 'bz2', 'xz' ]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -35,7 +35,7 @@ describe('Shared: SupportedFormats', function() {
|
||||
|
||||
it('should return the supported non compressed extensions', function() {
|
||||
const extensions = supportedFormats.getNonCompressedExtensions();
|
||||
m.chai.expect(extensions).to.deep.equal([ 'img', 'iso', 'dsk', 'hddimg', 'raw' ]);
|
||||
m.chai.expect(extensions).to.deep.equal([ 'img', 'iso', 'dsk', 'hddimg', 'raw', 'dmg' ]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -64,6 +64,92 @@ describe('Shared: SupportedFormats', function() {
|
||||
|
||||
describe('.isSupportedImage()', function() {
|
||||
|
||||
_.forEach([
|
||||
|
||||
// Type: 'archive'
|
||||
'path/to/filename.zip',
|
||||
'path/to/filename.etch',
|
||||
|
||||
// Type: 'compressed'
|
||||
'path/to/filename.img.gz',
|
||||
'path/to/filename.img.bz2',
|
||||
'path/to/filename.img.xz',
|
||||
|
||||
// Type: 'image'
|
||||
'path/to/filename.img',
|
||||
'path/to/filename.iso',
|
||||
'path/to/filename.dsk',
|
||||
'path/to/filename.hddimg',
|
||||
'path/to/filename.raw',
|
||||
'path/to/filename.dmg'
|
||||
|
||||
], (filename) => {
|
||||
it(`should return true for ${filename}`, function() {
|
||||
const isSupported = supportedFormats.isSupportedImage(filename);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if the file has no extension', function() {
|
||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
||||
m.chai.expect(isSupported).to.be.false;
|
||||
});
|
||||
|
||||
it('should return false if the extension is not included in .getAllExtensions()', function() {
|
||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo.jpg');
|
||||
m.chai.expect(isSupported).to.be.false;
|
||||
});
|
||||
|
||||
it('should return true if the extension is included in .getAllExtensions()', function() {
|
||||
const nonCompressedExtension = _.first(supportedFormats.getNonCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.${nonCompressedExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should ignore casing when determining extension validity', function() {
|
||||
const nonCompressedExtension = _.first(supportedFormats.getNonCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.${_.toUpper(nonCompressedExtension)}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should not consider an extension before a non compressed extension', function() {
|
||||
const nonCompressedExtension = _.first(supportedFormats.getNonCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.1234.${nonCompressedExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should return true if the extension is supported and the file name includes dots', function() {
|
||||
const nonCompressedExtension = _.first(supportedFormats.getNonCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.1.2.3-bar.${nonCompressedExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should return true if the extension is only a supported archive extension', function() {
|
||||
const archiveExtension = _.first(supportedFormats.getArchiveExtensions());
|
||||
const imagePath = `/path/to/foo.${archiveExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should return true if the extension is a supported one plus a supported compressed extensions', function() {
|
||||
const nonCompressedExtension = _.first(supportedFormats.getNonCompressedExtensions());
|
||||
const compressedExtension = _.first(supportedFormats.getCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.${nonCompressedExtension}.${compressedExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if the extension is an unsupported one plus a supported compressed extensions', function() {
|
||||
const compressedExtension = _.first(supportedFormats.getCompressedExtensions());
|
||||
const imagePath = `/path/to/foo.jpg.${compressedExtension}`;
|
||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||
m.chai.expect(isSupported).to.be.false;
|
||||
});
|
||||
|
||||
it('should return false if the file has no extension', function() {
|
||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
||||
m.chai.expect(isSupported).to.be.false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user