binary-version-reader
Advanced tools
Comparing version
@@ -88,3 +88,14 @@ const { Flags: ModuleFlags, MODULE_PREFIX_SIZE, MIN_MODULE_SUFFIX_SIZE, MAX_MODULE_SUFFIX_SIZE, ModuleInfoExtension, ModuleFunction } = require('./ModuleInfo.js'); | ||
/** | ||
* An error reported when assets are over the platform-specific limits | ||
*/ | ||
class AssetLimitError extends Error { | ||
constructor(details, ...args) { | ||
super(...args); | ||
this.name = this.constructor.name; | ||
this.details = details; | ||
} | ||
} | ||
// Size of the `compressed_module_header` structure in Device OS | ||
@@ -798,2 +809,8 @@ const COMPRESSED_MODULE_HEADER_SIZE = 8; | ||
if (moduleInfo && moduleInfo.maxSize) { | ||
if (buffer.length > moduleInfo.maxSize) { | ||
throw new AssetLimitError({}, 'Resulting module exceeds platform size limits'); | ||
} | ||
} | ||
updateModulePrefix(buffer, prefix); | ||
@@ -889,2 +906,91 @@ updateModuleSuffix(buffer, suffix); | ||
/** | ||
* Validate asset limits | ||
* | ||
* @param {Buffer|string} application Application binary buffer/path | ||
* @param {Array.<object>|Array.<string>} assets Asset data or paths | ||
* @returns {object} | ||
*/ | ||
async function validateAssetLimits(application, assets) { | ||
let app = {}; | ||
if (typeof application === 'string') { | ||
app.name = path.basename(application); | ||
app.data = await fsAsync.readFile(application); | ||
} else if (application.data && application.name) { | ||
app = application; | ||
} else { | ||
app = { | ||
data: application, | ||
name: 'application.bin' | ||
}; | ||
} | ||
const parser = new HalModuleParser(); | ||
const { prefixInfo: prefix } = await parser.parsePrefix({ fileBuffer: app.data }); | ||
prefix.moduleStartAddy = sanitizeAddress(prefix.moduleStartAddy); | ||
prefix.moduleEndAddy = sanitizeAddress(prefix.moduleEndAddy); | ||
const p = platformsById.get(prefix.platformID); | ||
if (!p) { | ||
throw new RangeError(`Unknown platform ID: ${prefix.platformID}`); | ||
} | ||
if (!p.assets) { | ||
throw new RangeError('This platform does not support assets'); | ||
} | ||
const storageLimit = p.assets.blockSize * (p.assets.storageTotalBlocks - p.assets.reservedBlocks); | ||
const errors = []; | ||
const processedAssets = []; | ||
let totalSize = 0; | ||
if (assets) { | ||
for (let asset of assets) { | ||
if (typeof asset === 'string') { | ||
const data = await fsAsync.readFile(asset); | ||
asset = { | ||
data: data, | ||
name: path.basename(asset) | ||
}; | ||
} | ||
const assetModule = await createAssetModule(asset.data, asset.name); | ||
const storageSize = p.assets.assetOverhead + Math.ceil((assetModule.length / (p.assets.blockSize - p.assets.assetPerBlockOverhead))) * p.assets.blockSize; | ||
totalSize += storageSize; | ||
processedAssets.push({ | ||
moduleSize: assetModule.length, | ||
storageSize, | ||
name: asset.name, | ||
originalSize: asset.data.length | ||
}); | ||
} | ||
} | ||
const details = { | ||
maxSingleAssetSize: p.assets.maxSingleAssetSize, | ||
maxTotalAssetSize: storageLimit, | ||
assets: processedAssets, | ||
totalAssetSize: totalSize, | ||
errors | ||
}; | ||
for (let asset of processedAssets) { | ||
if (asset.moduleSize > p.assets.maxSingleAssetSize) { | ||
errors.push(new AssetLimitError(details, `Asset ${asset.name} exceeds maximum single asset size limit by ${asset.moduleSize - details.maxSingleAssetSize} bytes`)); | ||
} | ||
} | ||
if (totalSize > storageLimit) { | ||
errors.push(new AssetLimitError(details, `Total size of assets exceeds platform limits by ${totalSize - storageLimit} bytes`)); | ||
} | ||
if (errors.length > 0) { | ||
// Throw first one, additional can be accessed in err.details.errors | ||
throw errors[0]; | ||
} | ||
return details; | ||
} | ||
/** | ||
* Create application and asset bundle | ||
@@ -927,3 +1033,12 @@ * | ||
if (processedAssets.length > 0) { | ||
updated = await updateModuleAssetDependencies(app.data, processedAssets); | ||
const details = await validateAssetLimits(app, processedAssets); | ||
try { | ||
updated = await updateModuleAssetDependencies(app.data, processedAssets); | ||
} catch (err) { | ||
if (err instanceof AssetLimitError) { | ||
err.details = details; | ||
err.details.errors.push(err); | ||
throw err; | ||
} | ||
} | ||
} else { | ||
@@ -1268,3 +1383,5 @@ updated = app.data; | ||
sanitizeAddress, | ||
isAssetValid | ||
isAssetValid, | ||
validateAssetLimits, | ||
AssetLimitError | ||
}; |
{ | ||
"name": "binary-version-reader", | ||
"version": "2.2.0", | ||
"version": "2.2.1", | ||
"main": "main.js", | ||
@@ -19,8 +19,9 @@ "license": "Apache-2.0", | ||
"peerDependencies": { | ||
"@particle/device-constants": "^3.1.11" | ||
"@particle/device-constants": "^3.2.0" | ||
}, | ||
"devDependencies": { | ||
"@particle/device-constants": "^3.1.11", | ||
"@particle/device-constants": "^3.2.0", | ||
"buffer-offset": "^0.1.2", | ||
"chai": "^4.0.0", | ||
"chai-as-promised": "^7.1.1", | ||
"chai-exclude": "^2.1.0", | ||
@@ -27,0 +28,0 @@ "coveralls": "^3.0.7", |
@@ -1,12 +0,11 @@ | ||
The Particle Binary Version Reader! | ||
===== | ||
# The Particle Binary Version Reader! | ||
If you're building firmware on the Particle Platform, you might be curious to see the metadata stored in your firmware! This module will read any metadata stored in the various modules (bootloader, system, user), and help you understand any dependencies. | ||
[](https://travis-ci.org/particle-iot/binary-version-reader) | ||
[](https://github.com/particle-iot/binary-version-reader/actions) | ||
Usage | ||
=== | ||
## Usage | ||
```js | ||
@@ -26,4 +25,3 @@ const Reader = require('binary-version-reader').HalModuleParser; | ||
Example output | ||
=== | ||
## Example output | ||
@@ -69,4 +67,4 @@ ```json | ||
``` | ||
const firmwareTestHelper = require('binary-version-reader'); | ||
const binary = firmwareTestHelper.createFirmwareBinary({ productId: 123, productVersion: 6, platformId: 10, depModuleVersion: 1210 }); | ||
const { firmwareTestHelper, ModuleInfo } = require('binary-version-reader'); | ||
const binary = firmwareTestHelper.createFirmwareBinary({ productId: 123, productVersion: 6, platformId: 10, deps: [ { func: ModuleInfo.FunctionType.SYSTEM_PART, index: 1, version: 1210 } ] }); | ||
``` | ||
@@ -73,0 +71,0 @@ |
@@ -16,3 +16,4 @@ const { | ||
sanitizeAddress, | ||
isAssetValid | ||
isAssetValid, | ||
AssetLimitError | ||
} = require('../../lib/moduleEncoding'); | ||
@@ -31,2 +32,5 @@ | ||
const chaiAsPromised = require('chai-as-promised'); | ||
chai.use(chaiAsPromised); | ||
const crypto = require('crypto'); | ||
@@ -703,2 +707,31 @@ const path = require('path'); | ||
}); | ||
it('throws an error when application binary with added asset dependencies exceeds platform size limit', async () => { | ||
const application = fs.readFileSync(path.join(TEST_BINARIES_PATH, 'tracker-max-size@5.5.0-rc.1.bin')); | ||
const assets = []; | ||
for (let i = 0; i < 100; i++) { | ||
assets.push({ | ||
data: genRandomBuffer(1), | ||
name: genRandomBuffer(127).toString('hex') | ||
}); | ||
} | ||
return expect(createApplicationAndAssetBundle(application, assets)).to.be.rejectedWith(AssetLimitError, 'Resulting module exceeds platform size limits'); | ||
}); | ||
it('throws an error when asset exceeds maximum single asset size limit on Tracker platform', async () => { | ||
const application = fs.readFileSync(path.join(TEST_BINARIES_PATH, 'tracker-tinker@5.3.1.bin')); | ||
return expect(createApplicationAndAssetBundle(application, [{ data: genRandomBuffer(10 * 1024 * 1024), name: 'test.bin' }])).to.be.rejectedWith(AssetLimitError, /maximum single asset size limit/); | ||
}); | ||
it('throws an error when assets exceed total maximum size of assets on Tracker platform', async () => { | ||
const application = fs.readFileSync(path.join(TEST_BINARIES_PATH, 'tracker-tinker@5.3.1.bin')); | ||
const assets = []; | ||
for (let i = 0; i < 10; i++) { | ||
assets.push({ | ||
data: genRandomBuffer(400 * 1024), | ||
name: genRandomBuffer(127).toString('hex') | ||
}); | ||
} | ||
return expect(createApplicationAndAssetBundle(application, assets)).to.be.rejectedWith(AssetLimitError, /Total size of assets exceeds/); | ||
}); | ||
}); | ||
@@ -705,0 +738,0 @@ |
1662089
0.3%5994
2.23%9
12.5%83
-2.35%