From 1808346fae11c27c771223d016bc1841c4e3c6e7 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 16 Dec 2015 14:51:17 +0100 Subject: [PATCH] Backed out changeset c472eeb03435 (bug 1214515) for bustage --HG-- extra : rebase_source : f0d8ce3fc4749b5fc398152ca26e17da7813dd09 --- b2g/components/PersistentDataBlock.jsm | 765 ------------------ b2g/components/moz.build | 5 - .../test/unit/file_persistentdatablock.js | 412 ---------- .../unit/test_persistentdatablock_gonk.js | 21 - b2g/components/test/unit/xpcshell.ini | 6 - 5 files changed, 1209 deletions(-) delete mode 100644 b2g/components/PersistentDataBlock.jsm delete mode 100644 b2g/components/test/unit/file_persistentdatablock.js delete mode 100644 b2g/components/test/unit/test_persistentdatablock_gonk.js diff --git a/b2g/components/PersistentDataBlock.jsm b/b2g/components/PersistentDataBlock.jsm deleted file mode 100644 index 16349cc24334..000000000000 --- a/b2g/components/PersistentDataBlock.jsm +++ /dev/null @@ -1,765 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * The Persistent Partition has this layout: - * - * Bytes: 32 4 4 1 - * Fields: [[DIGEST][MAGIC][DATA_LENGTH][ DATA ][OEM_UNLOCK_ENABLED]] - * - */ - -"use strict"; - -const DEBUG = false; - -this.EXPORTED_SYMBOLS = [ "PersistentDataBlock" ]; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -// This is a marker that will be written after digest in the partition. -const PARTITION_MAGIC = 0x19901873; -// This is the limit in Android because of issues with Binder if blocks are > 100k -// We dont really have this issues because we don't use Binder, but let's stick -// to Android implementation. -const MAX_DATA_BLOCK_SIZE = 1024 * 100; -const DIGEST_SIZE_BYTES = 32; -const HEADER_SIZE_BYTES = 8; -const PARTITION_MAGIC_SIZE_BYTES = 4; -const DATA_SIZE_BYTES = 4; -const OEM_UNLOCK_ENABLED_BYTES = 1; -// The position of the Digest -const DIGEST_OFFSET = 0; -const XPCOM_SHUTDOWN_OBSERVER_TOPIC = "xpcom-shutdown"; -// This property will have the path to the persistent partition -const PERSISTENT_DATA_BLOCK_PROPERTY = "ro.frp.pst"; -const OEM_UNLOCK_PROPERTY = "sys.oem_unlock_allowed"; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); - -XPCOMUtils.defineLazyGetter(this, "libcutils", function () { - Cu.import("resource://gre/modules/systemlibs.js"); - return libcutils; -}); - -var inParent = Cc["@mozilla.org/xre/app-info;1"] - .getService(Ci.nsIXULRuntime) - .processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - -function log(str) { - dump("PersistentDataBlock.jsm: " + str + "\n"); -} - -function debug(str) { - DEBUG && log(str); -} - -function toHexString(data) { - function toHexChar(charCode) { - return ("0" + charCode.toString(16).slice(-2)); - } - let hexString = ""; - if (typeof data === "string") { - hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join(""); - } else if (typeof data === "array") { - hexString = [toHexChar(data[i]) for (i in data)].join(""); - } - return hexString; -} - -function arr2bstr(arr) { - let bstr = ""; - for (let i = 0; i < arr.length; i++) { - bstr += String.fromCharCode(arr[i]); - } - return bstr; -} - -this.PersistentDataBlock = { - - /** - * libc funcionality. Accessed via ctypes - */ - _libc: { - handler: null, - open: function() {}, - close: function() {}, - ioctl: function() {} - }, - - /** - * Component to access property_get/set functions - */ - _libcutils: null, - - /** - * The size of a device block. This is assigned by querying the kernel. - */ - _blockDeviceSize: -1, - - /** - * Data block file - */ - _dataBlockFile: "", - - /** - * Change the behavior of the class for some methods to testing mode. This will fake the return value of some - * methods realted to native operations with block devices. - */ - _testing: false, - - /* - * *** USE ONLY FOR TESTING *** - * This component will interface between Gecko and a special secure partition with no formatting, a raw partition. - * This interaction requires a specific partition layout structure which emulators don't have so far. So for - * our unit tests to pass, we need a way for some methods to behave differently. This method will change this - * behavior at runtime so some low-level platform-specific operations will be faked: - * - Getting the size of a partition: We can use any partition to get the size, is up to the test to choose - * which partition to use. But, in testing mode we use files instead of partitions, so we need to fake the - * return value of this method in this case. - * - Wipping a partition: This will fully remove the partition as well as it filesystem type, so we cannot - * test it on any existing emulator partition. Testing mode will skip this operation. - * - * @param enabled {Bool} Set testing mode. See _testing property. - */ - setTestingMode: function(enabled) { - this._testing = enabled || false; - }, - - /** - * Initialize the class. - * - */ - init: function(mode) { - debug("init()"); - - if (libcutils) { - this._libcutils = libcutils; - } - - if (!this.ctypes) { - Cu.import("resource://gre/modules/ctypes.jsm", this); - } - - if (this._libc.handler === null) { -#ifdef MOZ_WIDGET_GONK - try { - this._libc.handler = this.ctypes.open(this.ctypes.libraryName("c")); - this._libc.close = this._libc.handler.declare("close", - this.ctypes.default_abi, - this.ctypes.int, - this.ctypes.int - ); - this._libc.open = this._libc.handler.declare("open", - this.ctypes.default_abi, - this.ctypes.int, - this.ctypes.char.ptr, - this.ctypes.int - ); - this._libc.ioctl = this._libc.handler.declare("ioctl", - this.ctypes.default_abi, - this.ctypes.int, - this.ctypes.int, - this.ctypes.unsigned_long, - this.ctypes.unsigned_long.ptr); - - } catch(ex) { - log("Unable to open libc.so: ex = " + ex); - throw Cr.NS_ERROR_FAILURE; - } -#else - log("This component requires Gonk!"); - throw Cr.NS_ERROR_ABORT; -#endif - } - - this._dataBlockFile = this._libcutils.property_get(PERSISTENT_DATA_BLOCK_PROPERTY); - if (this._dataBlockFile === null) { - log("init: ERROR: property " + PERSISTENT_DATA_BLOCK_PROPERTY + " doesn't exist!"); - throw Cr.NS_ERROR_FAILURE; - } - - Services.obs.addObserver(this, XPCOM_SHUTDOWN_OBSERVER_TOPIC, false); - }, - - uninit: function() { - debug("uninit()"); - this._libc.handler.close(); - Services.obs.removeObserver(this, XPCOM_SHUTDOWN_OBSERVER_TOPIC); - }, - - _checkLibcUtils: function() { - debug("_checkLibcUtils"); - if (!this._libcutils) { - log("No proper libcutils binding, aborting."); - throw Cr.NS_ERROR_NO_INTERFACE; - } - - return true; - }, - - /** - * Callback mehtod for addObserver - */ - observe: function(aSubject, aTopic, aData) { - debug("observe()"); - switch (aTopic) { - case XPCOM_SHUTDOWN_OBSERVER_TOPIC: - this.uninit(); - break; - - default: - log("Wrong observer topic: " + aTopic); - break; - } - }, - - /** - * This method will format the persistent partition if it detects manipulation (digest calculation will fail) - * or if the OEM Unlock Enabled byte is set to true. - * We need to call this method on every boot. - */ - start: function() { - debug("start()"); - return this._enforceChecksumValidity().then(() => { - return this._formatIfOemUnlockEnabled().then(() => { - return Promise.resolve(true); - }) - }).catch(ex => { - return Promise.reject(ex); - }); - }, - - /** - * Computes the digest of the entire data block. - * The digest is saved in the first 32 bytes of the block. - * - * @param isStoredDigestReturned {Bool} Tells the function to return the stored digest as well as the calculated. - * True means to return stored digest and the calculated - * False means to return just the calculated one - * - * @return Promise {Object} The calculated digest into the "calculated" property, and the stored - * digest into the "stored" property. - */ - _computeDigest: function (isStoredDigestReturned) { - debug("_computeDigest()"); - let digest = {calculated: "", stored: ""}; - let partition; - debug("_computeDigest: _dataBlockFile = " + this._dataBlockFile); - return OS.File.open(this._dataBlockFile, {existing:true, append:false, read:true}).then(_partition => { - partition = _partition; - return partition.read(DIGEST_SIZE_BYTES); - }).then(digestDataRead => { - // If storedDigest is passed as a parameter, the caller will likely compare the - // one is already stored in the partition with the one we are going to compute later. - if (isStoredDigestReturned === true) { - debug("_computeDigest: get stored digest from the partition"); - digest.stored = arr2bstr(digestDataRead); - } - return partition.read(); - }).then(data => { - // Calculate Digest with the data retrieved after the digest - let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); - hasher.init(hasher.SHA256); - hasher.update(data, data.byteLength); - digest.calculated = hasher.finish(false); - debug("_computeDigest(): Digest = " + toHexString(digest.calculated) + - "(" + digest.calculated.length + ")"); - return partition.close(); - }).then(() => { - return Promise.resolve(digest); - }).catch(ex => { - log("_computeDigest(): Failed to read partition: ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Returns the size of a block from the undelaying filesystem - * - * @return {Number} The size of the block - */ - _getBlockDeviceSize: function() { - debug("_getBlockDeviceSize()"); - - // See _testing property - if (this._testing === true) { - debug("_getBlockDeviceSize: No real block device size in testing mode!. Returning 1024."); - return 1024; - } - -#ifdef MOZ_WIDGET_GONK - const O_READONLY = 0; - const O_NONBLOCK = 1 << 11; - /* Getting the correct values for ioctl() operations by reading the headers is not a trivial task, so - * the better way to get the values below is by writting a simple test aplication in C that will - * print the values to the output. - * 32bits and 64bits value for ioctl() BLKGETSIZE64 operation is different. So we will fallback in - * case ioctl() returns ENOTTY (22). */ - const BLKGETSIZE64_32_BITS = 0x80041272; - const BLKGETSIZE64_64_BITS = 0x80081272; - const ENOTTY = 25; - - debug("_getBlockDeviceSize: _dataBlockFile = " + this._dataBlockFile); - let fd = this._libc.open(this._dataBlockFile, O_READONLY | O_NONBLOCK); - if (fd < 0) { - log("_getBlockDeviceSize: couldn't open partition!: errno = " + this.ctypes.errno); - throw Cr.NS_ERROR_FAILURE; - } - - let size = new this.ctypes.unsigned_long(); - let sizeAddress = size.address(); - let ret = this._libc.ioctl(fd, BLKGETSIZE64_32_BITS, sizeAddress); - if (ret < 0) { - if (this.ctypes.errno === ENOTTY) { - log("_getBlockDeviceSize: errno is ENOTTY, falling back to 64 bit version of BLKGETSIZE64..."); - ret = this._libc.ioctl(fd, BLKGETSIZE64_64_BITS, sizeAddress); - if (ret < 0) { - this._libc.close(fd); - log("_getBlockDeviceSize: BLKGETSIZE64 failed again!. errno = " + this.ctypes.errno); - throw Cr.NS_ERROR_FAILURE; - } - } else { - this._libc.close(fd); - log("_getBlockDeviceSize: couldn't get block device size!: errno = " + this.ctypes.errno); - throw Cr.NS_ERROR_FAILURE; - } - } - this._libc.close(fd); - debug("_getBlockDeviceSize: size =" + size.value); - return size.value; -#else - log("_getBlockDeviceSize: ERROR: This feature is only supported in Gonk!"); - return -1; -#endif - }, - - /** - * Sets the byte into the partition which represents the OEM Unlock Enabled feature. - * A value of "1" means that the user doesn't want to enable KillSwitch. - * The byte is the last one byte into the device block. - * - * @param isSetOemUnlockEnabled {bool} If true, sets the OEM Unlock Enabled byte to 1. - * Otherwise, sets it to 0. - */ - _doSetOemUnlockEnabled: function(isSetOemUnlockEnabled) { - debug("_doSetOemUnlockEnabled()"); - let partition; - return OS.File.open(this._dataBlockFile, {existing:true, append:false, write:true}).then(_partition => { - partition = _partition; - return partition.setPosition(this._getBlockDeviceSize() - OEM_UNLOCK_ENABLED_BYTES, OS.File.POS_START); - }).then(() => { - return partition.write(new Uint8Array([ isSetOemUnlockEnabled === true ? 1 : 0 ])); - }).then(bytesWrittenLength => { - if (bytesWrittenLength != 1) { - log("_doSetOemUnlockEnabled: Error writting OEM Unlock Enabled byte!"); - return Promise.reject(); - } - return partition.close(); - }).then(() => { - let oemUnlockByte = (isSetOemUnlockEnabled === true ? "1" : "0"); - debug("_doSetOemUnlockEnabled: OEM unlock enabled written to " + oemUnlockByte); - this._libcutils.property_set(OEM_UNLOCK_PROPERTY, oemUnlockByte); - return Promise.resolve(); - }).catch(ex => { - return Promise.reject(ex); - }); - }, - - /** - * Computes the digest by reading the entire block of data and write it to the digest field - * - * @return true Promise Operation succeed - * @return false Promise Operation failed - */ - _computeAndWriteDigest: function() { - debug("_computeAndWriteDigest()"); - let digest; - let partition; - return this._computeDigest().then(_digest => { - digest = _digest; - return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false}); - }).then(_partition => { - partition = _partition; - return partition.setPosition(DIGEST_OFFSET, OS.File.POS_START); - }).then(() => { - return partition.write(new Uint8Array([digest.calculated.charCodeAt(i) for (i in digest.calculated)])); - }).then(bytesWrittenLength => { - if (bytesWrittenLength != DIGEST_SIZE_BYTES) { - log("_computeAndWriteDigest: Error writting digest to partition!. Expected: " + DIGEST_SIZE_BYTES + " Written: " + bytesWrittenLength); - return Promise.reject(); - } - return partition.close(); - }).then(() => { - debug("_computeAndWriteDigest: digest written to partition"); - return Promise.resolve(true); - }).catch(ex => { - log("_computeAndWriteDigest: Couldn't write digest in the persistent partion. ex = " + ex ); - return Promise.reject(ex); - }); - }, - - /** - * Formats the persistent partition if the OEM Unlock Enabled field is set to true, and - * write the Unlock Property accordingly. - * - * @return true Promise OEM Unlock was enabled, so the partition has been formated - * @return false Promise OEM Unlock was disabled, so the partition hasn't been formated - */ - _formatIfOemUnlockEnabled: function () { - debug("_formatIfOemUnlockEnabled()"); - return this.getOemUnlockEnabled().then(enabled => { - this._libcutils.property_set(OEM_UNLOCK_PROPERTY,(enabled === true ? "1" : "0")); - if (enabled === true) { - return this._formatPartition(true); - } - return Promise.resolve(false); - }).then(result => { - if (result === false) { - return Promise.resolve(false); - } else { - return Promise.resolve(true); - } - }).catch(ex => { - log("_formatIfOemUnlockEnabled: An error ocurred!. ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Formats the persistent data partition with the proper structure. - * - * @param isSetOemUnlockEnabled {bool} If true, writes a "1" in the OEM Unlock Enabled field (last - * byte of the block). If false, writes a "0". - * - * @return Promise - */ - _formatPartition: function(isSetOemUnlockEnabled) { - debug("_formatPartition()"); - let partition; - return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false}).then(_partition => { - partition = _partition; - return partition.write(new Uint8Array(DIGEST_SIZE_BYTES)); - }).then(bytesWrittenLength => { - if (bytesWrittenLength != DIGEST_SIZE_BYTES) { - log("_formatPartition Error writting zero-digest!. Expected: " + DIGEST_SIZE_BYTES + " Written: " + bytesWrittenLength); - return Promise.reject(); - } - return partition.write(new Uint32Array([PARTITION_MAGIC])); - }).then(bytesWrittenLength => { - if (bytesWrittenLength != PARTITION_MAGIC_SIZE_BYTES) { - log("_formatPartition Error writting magic number!. Expected: " + PARTITION_MAGIC_SIZE_BYTES + " Written: " + bytesWrittenLength); - return Promise.reject(); - } - return partition.write(new Uint8Array(DATA_SIZE_BYTES)); - }).then(bytesWrittenLength => { - if (bytesWrittenLength != DATA_SIZE_BYTES) { - log("_formatPartition Error writting data size!. Expected: " + DATA_SIZE_BYTES + " Written: " + bytesWrittenLength); - return Promise.reject(); - } - return partition.close(); - }).then(() => { - return this._doSetOemUnlockEnabled(isSetOemUnlockEnabled); - }).then(() => { - return this._computeAndWriteDigest(); - }).then(() => { - return Promise.resolve(); - }).catch(ex => { - log("_formatPartition: Failed to format block device!: ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Check digest validity. If it's not valid, formats the persistent partition - * - * @return true Promise The checksum is valid so the promise is resolved to true - * @return false Promise The checksum is not valid, so the partition is going to be - * formatted and the OEM Unlock Enabled field written to 0 (false). - */ - _enforceChecksumValidity: function() { - debug("_enforceChecksumValidity"); - return this._computeDigest(true).then(digest => { - if (digest.stored != digest.calculated) { - log("_enforceChecksumValidity: Validation failed! Stored digest: " + toHexString(digest.stored) + - " is not the same as the calculated one: " + toHexString(digest.calculated)); - return Promise.reject(); - } - debug("_enforceChecksumValidity: Digest computation succeed."); - return Promise.resolve(true); - }).catch(ex => { - log("_enforceChecksumValidity: Digest computation failed: ex = " + ex); - log("_enforceChecksumValidity: Formatting FRP partition..."); - return this._formatPartition(false).then(() => { - return Promise.resolve(false); - }).catch(ex => { - log("_enforceChecksumValidity: Error ocurred while formating the partition!: ex = " + ex); - return Promise.reject(ex); - }); - }); - }, - - /** - * Reads the entire data field - * - * @return bytes Promise A promise resolved with the bytes read - */ - read: function() { - debug("read()"); - let partition; - let bytes; - let dataSize; - return this.getDataFieldSize().then(_dataSize => { - dataSize = _dataSize; - return OS.File.open(this._dataBlockFile, {read:true, existing:true, append:false}); - }).then(_partition => { - partition = _partition; - return partition.setPosition(DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES, OS.File.POS_START); - }).then(() => { - return partition.read(dataSize); - }).then(_bytes => { - bytes = _bytes; - if (bytes.byteLength < dataSize) { - log("read: Failed to read entire data block. Bytes read: " + bytes.byteLength + "/" + dataSize); - return Promise.reject(); - } - return partition.close(); - }).then(() => { - return Promise.resolve(bytes); - }).catch(ex => { - log("read: Failed to read entire data block. Exception: " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Writes an entire block to the persistent partition - * - * @param data {Uint8Array} - * - * @return Promise Promise resolved to the number of bytes written. - */ - write: function(data) { - debug("write()"); - // Ensure that we don't overwrite digest/magic/data-length and the last byte - let maxBlockSize = this._getBlockDeviceSize() - (DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES + 1); - if (data.byteLength > maxBlockSize) { - log("write: Couldn't write more than " + maxBlockSize + " bytes to the partition. " + - maxBlockSize + " bytes given."); - return Promise.reject(); - } - - let partition; - return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false}).then(_partition => { - let digest = new Uint8Array(DIGEST_SIZE_BYTES); - let magic = new Uint8Array((new Uint32Array([PARTITION_MAGIC])).buffer); - let dataLength = new Uint8Array((new Uint32Array([data.byteLength])).buffer); - let bufferToWrite = new Uint8Array(digest.byteLength + magic.byteLength + dataLength.byteLength + data.byteLength ); - let offset = 0; - bufferToWrite.set(digest, offset); - offset += digest.byteLength; - bufferToWrite.set(magic, offset); - offset += magic.byteLength; - bufferToWrite.set(dataLength, offset); - offset += dataLength.byteLength; - bufferToWrite.set(data, offset); - partition = _partition; - return partition.write(bufferToWrite); - }).then(bytesWrittenLength => { - let expectedWrittenLength = DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES + data.byteLength; - if (bytesWrittenLength != expectedWrittenLength) { - log("write: Error writting data to partition!: Expected: " + expectedWrittenLength + " Written: " + bytesWrittenLength); - return Promise.reject(); - } - return partition.close(); - }).then(() => { - return this._computeAndWriteDigest(); - }).then(couldComputeAndWriteDigest => { - if (couldComputeAndWriteDigest === true) { - return Promise.resolve(data.byteLength); - } else { - log("write: Failed to compute and write the digest"); - return Promise.reject(); - } - }).catch(ex => { - log("write: Failed to write to the persistent partition: ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Wipes the persistent partition. - * - * @return Promise If no errors, the promise is resolved - */ - wipe: function() { - debug("wipe()"); - - if (this._testing === true) { - log("wipe: No wipe() funcionality in testing mode"); - return Promise.resolve(); - } - -#ifdef MOZ_WIDGET_GONK - const O_READONLY = 0; - const O_RDWR = 2; - const O_NONBLOCK = 1 << 11; - // This constant value is the same under 32 and 64 bits arch. - const BLKSECDISCARD = 0x127D; - // This constant value is the same under 32 and 64 bits arch. - const BLKDISCARD = 0x1277; - - return new Promise((resolve, reject) => { - let range = new this.ctypes.unsigned_long(); - let rangeAddress = range.address(); - let blockDeviceLength = this._getBlockDeviceSize(); - range[0] = 0; - range[1] = blockDeviceLength; - if (range[1] === 0) { - log("wipe: Block device size is 0!"); - return reject(); - } - let fd = this._libc.open(this._dataBlockFile, O_RDWR); - if (fd < 0) { - log("wipe: ERROR couldn't open partition!: error = " + this.ctypes.errno); - return reject(); - } - let ret = this._libc.ioctl(fd, BLKSECDISCARD, rangeAddress); - if (ret < 0) { - log("wipe: Something went wrong secure discarding block: errno: " + this.ctypes.errno + ": Falling back to non-secure discarding..."); - ret = this._libc.ioctl(fd, BLKDISCARD, rangeAddress); - if (ret < 0) { - this._libc.close(fd); - log("wipe: CRITICAL: non-secure discarding failed too!!: errno: " + this.ctypes.errno); - return reject(); - } else { - this._libc.close(fd); - log("wipe: non-secure discard used and succeed"); - return resolve(); - } - } - this._libc.close(fd); - log("wipe: secure discard succeed"); - return resolve(); - }); -#else - log("wipe: ERROR: This feature is only supported in Gonk!"); - return Promise.reject(); -#endif - }, - - /** - * Set the OEM Unlock Enabled field (one byte at the end of the partition), to 1 or 0 depending on - * the input parameter. - * - * @param enabled {bool} If enabled, we write a 1 in the last byte of the partition. - * - * @return Promise - * - */ - setOemUnlockEnabled: function(enabled) { - debug("setOemUnlockEnabled()"); - return this._doSetOemUnlockEnabled(enabled).then(() => { - return this._computeAndWriteDigest(); - }).then(() => { - return Promise.resolve(); - }).catch(ex => { - return Promise.reject(ex); - }); - }, - - /** - * Gets the byte from the partition which represents the OEM Unlock Enabled state. - * - * @return true Promise The user didn't activate KillSwitch. - * @return false Promise The user did activate KillSwitch. - */ - getOemUnlockEnabled: function() { - log("getOemUnlockEnabled()"); - let ret = false; - let partition; - return OS.File.open(this._dataBlockFile, {existing:true, append:false, read:true}).then(_partition => { - partition = _partition; - return partition.setPosition(this._getBlockDeviceSize() - OEM_UNLOCK_ENABLED_BYTES, OS.File.POS_START); - }).then(() => { - return partition.read(OEM_UNLOCK_ENABLED_BYTES); - }).then(data => { - debug("getOemUnlockEnabled: OEM unlock enabled byte = '" + data[0] + "'"); - ret = (data[0] === 1 ? true : false); - return partition.close(); - }).then(() => { - return Promise.resolve(ret); - }).catch(ex => { - log("getOemUnlockEnabled: Error reading OEM unlock enabled byte from partition: ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Gets the size of the data block by reading the data-length field - * - * @return Promise A promise resolved to the number of bytes os the data field. - */ - getDataFieldSize: function() { - debug("getDataFieldSize()"); - let partition - let dataLength = 0; - return OS.File.open(this._dataBlockFile, {read:true, existing:true, append:false}).then(_partition => { - partition = _partition; - // Skip the digest field - return partition.setPosition(DIGEST_SIZE_BYTES, OS.File.POS_START); - }).then(() => { - // Read the Magic field - return partition.read(PARTITION_MAGIC_SIZE_BYTES); - }).then(_magic => { - let magic = new Uint32Array(_magic.buffer)[0]; - if (magic === PARTITION_MAGIC) { - return partition.read(PARTITION_MAGIC_SIZE_BYTES); - } else { - log("getDataFieldSize: ERROR: Invalid Magic number!"); - return Promise.reject(); - } - }).then(_dataLength => { - if (_dataLength) { - dataLength = new Uint32Array(_dataLength.buffer)[0]; - } - return partition.close(); - }).then(() => { - if (dataLength && dataLength != 0) { - return Promise.resolve(dataLength); - } else { - return Promise.reject(); - } - }).catch(ex => { - log("getDataFieldSize: Couldn't get data field size: ex = " + ex); - return Promise.reject(ex); - }); - }, - - /** - * Gets the maximum possible size of a data field - * - * @return Promise A Promise resolved to the maximum number of bytes allowed for the data field - * - */ - getMaximumDataBlockSize: function() { - debug("getMaximumDataBlockSize()"); - return new Promise((resolve, reject) => { - let actualSize = this._getBlockDeviceSize() - HEADER_SIZE_BYTES - OEM_UNLOCK_ENABLED_BYTES; - resolve(actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE); - }); - } - -}; - -// This code should ALWAYS be living only on the parent side. -if (!inParent) { - log("PersistentDataBlock should only be living on parent side."); - throw Cr.NS_ERROR_ABORT; -} else { - this.PersistentDataBlock.init(); -} diff --git a/b2g/components/moz.build b/b2g/components/moz.build index ddaf63487cd0..31d7982aba27 100644 --- a/b2g/components/moz.build +++ b/b2g/components/moz.build @@ -78,11 +78,6 @@ EXTRA_JS_MODULES += [ 'WebappsUpdater.jsm', ] -EXTRA_PP_JS_MODULES += [ - 'KillSwitchMain.jsm', - 'PersistentDataBlock.jsm' -] - if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk': EXTRA_JS_MODULES += [ 'GlobalSimulatorScreen.jsm' diff --git a/b2g/components/test/unit/file_persistentdatablock.js b/b2g/components/test/unit/file_persistentdatablock.js deleted file mode 100644 index de913c3ef8e0..000000000000 --- a/b2g/components/test/unit/file_persistentdatablock.js +++ /dev/null @@ -1,412 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -// This constants must be synced with the ones in PersistentDataBlock.jsm -const PARTITION_MAGIC = 0x19901873; -const DIGEST_SIZE_BYTES = 32; -const PARTITION_MAGIC_SIZE_BYTES = 4; -const DATA_SIZE_BYTES = 4; -const OEM_UNLOCK_ENABLED_BYTES = 1; - -const CACHE_PARTITION = "/dev/block/mtdblock2"; -const PARTITION_FAKE_FILE = "/data/local/tmp/frp.test"; -const CACHE_PARTITION_SIZE = 69206016; - -function log(str) { - do_print("head_persistentdatablock: " + str + "\n"); -} - -function toHexString(data) { - function toHexChar(charCode) { - return ("0" + charCode.toString(16).slice(-2)); - } - let hexString = ""; - if (typeof data === "string") { - hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join(""); - } else if (typeof data === "array") { - hexString = [toHexChar(data[i]) for (i in data)].join(""); - } - return hexString; -} - -function _prepareConfig(_args) { - let args = _args || {}; - // This digest has been previously calculated given the data to be written later, and setting the OEM Unlocked Enabled byte - // to 1. If we need different values, some tests will fail because this precalculated digest won't be valid then. - args.digest = args.digest || new Uint8Array([0x00, 0x41, 0x7e, 0x5f, 0xe2, 0xdd, 0xaa, 0xed, 0x11, 0x90, 0x0e, 0x1d, 0x26, - 0x10, 0x30, 0xbd, 0x44, 0x9e, 0xcc, 0x4b, 0x65, 0xbe, 0x2e, 0x99, 0x9f, 0x86, - 0xf0, 0xfc, 0x5b, 0x33, 0x00, 0xd0]); - args.dataLength = args.dataLength || 6; - args.data = args.data || new Uint8Array(["P", "A", "S", "S", "W", "D"]); - args.oem = args.oem === undefined ? true : args.oem; - args.oemUnlockAllowed = args.oemUnlockAllowed === undefined ? true : args.oemUnlockAllowed; - - log("_prepareConfig: args.digest = " + args.digest); - log("_prepareConfig: args.dataLength = " + args.dataLength); - log("_prepareConfig: args.data = " + args.data); - log("_prepareConfig: args.oem = " + args.oem); - log("_prepareConfig: args.oemUnlockAllowed = " + args.oemUnlockAllowed); - - /* This function will be called after passing all native stuff tests, so we will write into a file instead of a real - * partition. Obviously, there are some native operations like getting the device block size or wipping, that will not - * work in a regular file, so we need to fake them. */ - PersistentDataBlock._libcutils.property_set("sys.oem_unlock_allowed", args.oemUnlockAllowed === true ? "true" : "false"); - PersistentDataBlock.setTestingMode(true); - PersistentDataBlock._dataBlockFile = PARTITION_FAKE_FILE; - // Create the test file with the same structure as the partition will be - let tempFile; - return OS.File.open(PersistentDataBlock._dataBlockFile, {write:true, append:false, truncate: true}).then(_tempFile => { - log("_prepareConfig: Writing DIGEST..."); - tempFile = _tempFile; - return tempFile.write(args.digest); - }).then(bytes => { - log("_prepareConfig: Writing the magic: " + PARTITION_MAGIC); - return tempFile.write(new Uint32Array([PARTITION_MAGIC])); - }).then(bytes => { - log("_prepareConfig: Writing the length of data field"); - return tempFile.write(new Uint32Array([args.dataLength])); - }).then(bytes => { - log("_prepareConfig: Writing the data field"); - let data = new Uint8Array(PersistentDataBlock._getBlockDeviceSize() - - (DIGEST_SIZE_BYTES + PARTITION_MAGIC_SIZE_BYTES + DATA_SIZE_BYTES + OEM_UNLOCK_ENABLED_BYTES)); - data.set(args.data); - return tempFile.write(data); - }).then(bytes => { - return tempFile.write(new Uint8Array([ args.oem === true ? 1 : 0 ])); - }).then(bytes => { - return tempFile.close(); - }).then(() =>{ - return Promise.resolve(true); - }).catch(ex => { - log("_prepareConfig: ERROR: ex = " + ex); - return Promise.reject(ex); - }); -} - -function utils_getByteAt(pos) { - let file; - let byte; - return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => { - file = _file; - return file.setPosition(pos, OS.File.POS_START); - }).then(() => { - return file.read(1); - }).then(_byte => { - byte = _byte; - return file.close(); - }).then(() => { - return Promise.resolve(byte[0]); - }).catch(ex => { - return Promise.reject(ex); - }); -} - -function utils_getHeader() { - let file; - let header = {}; - return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => { - file = _file; - return file.read(DIGEST_SIZE_BYTES); - }).then(digest => { - header.digest = digest; - return file.read(PARTITION_MAGIC_SIZE_BYTES); - }).then(magic => { - header.magic = magic; - return file.read(DATA_SIZE_BYTES); - }).then(dataLength => { - header.dataLength = dataLength; - return file.close(); - }).then(() => { - return Promise.resolve(header); - }).catch(ex => { - return Promise.reject(ex); - }); -} - -function utils_getData() { - let file; - let data; - return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => { - file = _file; - return file.setPosition(DIGEST_SIZE_BYTES + PARTITION_MAGIC_SIZE_BYTES, OS.File.POS_START); - }).then(() => { - return file.read(4); - }).then(_dataLength => { - let dataLength = new Uint32Array(_dataLength.buffer); - log("utils_getData: dataLength = " + dataLength[0]); - return file.read(dataLength[0]); - }).then(_data => { - data = _data; - return file.close(); - }).then(() => { - return Promise.resolve(data); - }).catch(ex => { - return Promise.reject(ex); - }); -} - -function _installTests() { - // Native operation tests go first - add_test(function test_getBlockDeviceSize() { - // We will use emulator /cache partition to get it's size. - PersistentDataBlock._dataBlockFile = CACHE_PARTITION; - // Disable testing mode for this specific test because we can get the size of a real block device, - // but we need to flip to testing mode after this test because we use files instead of partitions - // and we cannot run this operation on files. - PersistentDataBlock.setTestingMode(false); - let blockSize = PersistentDataBlock._getBlockDeviceSize(); - ok(blockSize !== CACHE_PARTITION_SIZE, "test_getBlockDeviceSize: Block device size should be greater than 0"); - run_next_test(); - }); - - add_test(function test_wipe() { - // Turning into testing mode again. - PersistentDataBlock.setTestingMode(true); - PersistentDataBlock.wipe().then(() => { - // We don't evaluate anything because in testing mode we always return ok! - run_next_test(); - }).catch(ex => { - // ... something went really really bad if this happens. - ok(false, "test_wipe failed!: ex: " + ex); - }); - }); - // - - add_test(function test_computeDigest() { - _prepareConfig().then(() => { - PersistentDataBlock._computeDigest().then(digest => { - // So in order to update this value in a future (should only happens if the partition data is changed), you just need - // to launch this test manually, see the result in the logs and update this constant with that value. - const _EXPECTED_VALUE = "0004107e05f0e20dd0aa0ed0110900e01d0260100300bd04409e0cc04b0650be02e09909f0860f00fc05b033000d0"; - let calculatedValue = toHexString(digest.calculated); - strictEqual(calculatedValue, _EXPECTED_VALUE); - run_next_test(); - }).catch(ex => { - ok(false, "test_computeDigest failed!: ex: " + ex); - }); - }); - }); - - add_test(function test_getDataFieldSize() { - PersistentDataBlock.getDataFieldSize().then(dataFieldLength => { - log("test_getDataFieldSize: dataFieldLength is " + dataFieldLength); - strictEqual(dataFieldLength, 6); - run_next_test(); - }).catch(ex => { - ok(false, "test_getOemUnlockedEnabled failed: ex:" + ex); - }); - }); - - add_test(function test_setOemUnlockedEnabledToTrue() { - PersistentDataBlock.setOemUnlockEnabled(true).then(() => { - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - log("test_setOemUnlockedEnabledToTrue: byte = " + byte ); - strictEqual(byte, 1); - run_next_test(); - }).catch(ex => { - ok(false, "test_setOemUnlockedEnabledToTrue failed!: ex: " + ex); - }); - }); - - add_test(function test_setOemUnlockedEnabledToFalse() { - PersistentDataBlock.setOemUnlockEnabled(false).then(() => { - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - log("test_setOemUnlockedEnabledToFalse: byte = " + byte ); - strictEqual(byte, 0); - run_next_test(); - }).catch(ex => { - ok(false, "test_setOemUnlockedEnabledToFalse failed!: ex: " + ex); - }); - }); - - add_test(function test_getOemUnlockedEnabledWithTrue() { - // We first need to set the OEM Unlock Enabled byte to true so we can test - // the getter properly - PersistentDataBlock.setOemUnlockEnabled(true).then(() => { - return PersistentDataBlock.getOemUnlockEnabled().then(enabled => { - log("test_getOemUnlockedEnabledWithTrue: enabled is " + enabled); - ok(enabled === true, "test_getOemUnlockedEnabledWithTrue: enabled value should be true"); - run_next_test(); - }).catch(ex => { - ok(false, "test_getOemUnlockedEnabledWithTrue failed: ex:" + ex); - }); - }).catch(ex => { - ok(false, "test_getOemUnlockedEnabledWithTrue failed: An error ocurred while setting the OEM Unlock Enabled byte to true: ex:" + ex); - }); - }); - - add_test(function test_getOemUnlockedEnabledWithFalse() { - // We first need to set the OEM Unlock Enabled byte to false so we can test - // the getter properly - PersistentDataBlock.setOemUnlockEnabled(false).then(() => { - return PersistentDataBlock.getOemUnlockEnabled().then(enabled => { - log("test_getOemUnlockedEnabledWithFalse: enabled is " + enabled); - ok(enabled === false, "test_getOemUnlockedEnabledWithFalse: enabled value should be false"); - run_next_test(); - }).catch(ex => { - ok(false, "test_getOemUnlockedEnabledWithFalse failed: ex:" + ex); - }); - }).catch(ex => { - ok(false, "test_getOemUnlockedEnabledWithFalse failed: An error ocurred while setting the OEM Unlock Enabled byte to false: ex:" + ex); - }); - }); - - add_test(function test_computeAndWriteDigest() { - PersistentDataBlock._computeAndWriteDigest().then(() => { - return utils_getHeader(); - }).then(header => { - log("test_computeAndWriteDigest: header = " + header); - let magicRead = new Uint32Array(header.magic.buffer); - let magicSupposed = new Uint32Array([PARTITION_MAGIC]); - strictEqual(magicRead[0], magicSupposed[0]); - let dataLength = new Uint32Array([header.dataLength]); - strictEqual(header.dataLength[0], 6); - run_next_test(); - }).catch(ex => { - ok(false, "test_computeAndWriteDigest failed!: ex: " + ex); - }); - }); - - add_test(function test_formatIfOemUnlockEnabledWithTrue() { - _prepareConfig({oem:true}).then(() => { - return PersistentDataBlock._formatIfOemUnlockEnabled(); - }).then(result => { - ok(result === true, "test_formatIfOemUnlockEnabledWithTrue: result should be true"); - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - // Check if the OEM Unlock Enabled byte is 1 - strictEqual(byte, 1); - run_next_test(); - }).catch(ex => { - ok(false, "test_formatIfOemUnlockEnabledWithTrue failed!: ex: " + ex); - }); - }); - - add_test(function test_formatIfOemUnlockEnabledWithFalse() { - _prepareConfig({oem:false}).then(() => { - return PersistentDataBlock._formatIfOemUnlockEnabled(); - }).then(result => { - log("test_formatIfOemUnlockEnabledWithFalse: result = " + result); - ok(result === false, "test_formatIfOemUnlockEnabledWithFalse: result should be false"); - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - // Check if the OEM Unlock Enabled byte is 0 - strictEqual(byte, 0); - run_next_test(); - }).catch(ex => { - ok(false, "test_formatIfOemUnlockEnabledWithFalse failed!: ex: " + ex); - }); - }); - - add_test(function test_formatPartition() { - // Restore a fullfilled partition so we can check if formatting works... - _prepareConfig({oem:true}).then(() => { - return PersistentDataBlock._formatPartition(true); - }).then(() => { - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - // Check if the last byte is 1 - strictEqual(byte, 1); - return utils_getHeader(); - }).then(header => { - // The Magic number should exists in a formatted partition - let magicRead = new Uint32Array(header.magic.buffer); - let magicSupposed = new Uint32Array([PARTITION_MAGIC]); - strictEqual(magicRead[0], magicSupposed[0]); - // In a formatted partition, the digest field is always 32 bytes of zeros. - let digestSupposed = new Uint8Array(DIGEST_SIZE_BYTES); - strictEqual(header.digest.join(""), "94227253995810864198417798821014713171138121254110134189198178208133167236184116199"); - return PersistentDataBlock._formatPartition(false); - }).then(() => { - return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1); - }).then(byte => { - // In this case OEM Unlock enabled byte should be set to 0 because we passed false to the _formatPartition method before. - strictEqual(byte, 0); - run_next_test(); - }).catch(ex => { - ok(false, "test_formatPartition failed!: ex: " + ex); - }); - }); - - add_test(function test_enforceChecksumValidityWithValidChecksum() { - // We need a valid partition layout to pass this test - _prepareConfig().then(() => { - PersistentDataBlock._enforceChecksumValidity().then(() => { - ok(true, "test_enforceChecksumValidityWithValidChecksum passed"); - run_next_test(); - }).catch(ex => { - ok(false, "test_enforceChecksumValidityWithValidChecksum failed!: ex: " + ex); - }); - }); - }); - - add_test(function test_enforceChecksumValidityWithInvalidChecksum() { - var badDigest = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F, 0x20]); - // We need a valid partition layout to pass this test - _prepareConfig({digest: badDigest}).then(() => { - PersistentDataBlock._enforceChecksumValidity().then(() => { - return utils_getHeader(); - }).then(header => { - // Check that we have a valid magic after formatting - let magicRead = new Uint32Array(header.magic.buffer)[0]; - let magicSupposed = new Uint32Array([PARTITION_MAGIC])[0]; - strictEqual(magicRead, magicSupposed); - // Data length field should be 0, because we formatted the partition - let dataLengthRead = new Uint32Array(header.dataLength.buffer)[0]; - strictEqual(dataLengthRead, 0); - run_next_test(); - }).catch(ex => { - ok(false, "test_enforceChecksumValidityWithValidChecksum failed!: ex: " + ex); - }); - }); - }); - - add_test(function test_read() { - // Before reading, let's write some bytes of data first. - PersistentDataBlock.write(new Uint8Array([1,2,3,4])).then(() => { - PersistentDataBlock.read().then(bytes => { - log("test_read: bytes (in hex): " + toHexString(bytes)); - strictEqual(bytes[0], 1); - strictEqual(bytes[1], 2); - strictEqual(bytes[2], 3); - strictEqual(bytes[3], 4); - run_next_test(); - }).catch(ex => { - ok(false, "test_read failed!: ex: " + ex); - }); - }); - - }); - - add_test(function test_write() { - let data = new Uint8Array(['1','2','3','4','5']); - PersistentDataBlock.write(data).then(bytesWrittenLength => { - log("test_write: bytesWrittenLength = " + bytesWrittenLength); - return utils_getData(); - }).then(data => { - strictEqual(data[0], 1); - strictEqual(data[1], 2); - strictEqual(data[2], 3); - strictEqual(data[3], 4); - strictEqual(data[4], 5); - run_next_test(); - }).catch(ex => { - ok(false, "test_write failed!: ex: " + ex); - }); - }); -} diff --git a/b2g/components/test/unit/test_persistentdatablock_gonk.js b/b2g/components/test/unit/test_persistentdatablock_gonk.js deleted file mode 100644 index 067eb83e3344..000000000000 --- a/b2g/components/test/unit/test_persistentdatablock_gonk.js +++ /dev/null @@ -1,21 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "libcutils", function () { - Cu.import("resource://gre/modules/systemlibs.js"); - return libcutils; -}); - -function run_test() { - do_get_profile(); - Cu.import("resource://gre/modules/PersistentDataBlock.jsm"); - // We need to point to a valid partition for some of the tests. This is the /cache - // partition in the emulator (x86-KitaKat). - run_next_test(); -} - -_installTests(); diff --git a/b2g/components/test/unit/xpcshell.ini b/b2g/components/test/unit/xpcshell.ini index 35b6261f2768..9897ac072aca 100644 --- a/b2g/components/test/unit/xpcshell.ini +++ b/b2g/components/test/unit/xpcshell.ini @@ -50,9 +50,3 @@ skip-if = (toolkit == "gonk") head = file_killswitch.js # Bug 1193677: disable on B2G ICS Emulator for intermittent failures with IndexedDB skip-if = ((toolkit != "gonk") || (toolkit == "gonk" && debug)) - -[test_persistentdatablock_gonk.js] -head = file_persistentdatablock.js -skip-if = (toolkit != "gonk") - -