static int ca8210_remove(struct spi_device *spi_device)
{
	struct ca8210_priv *priv;
	struct ca8210_platform_data *pdata;

	dev_info(&spi_device->dev, "Removing ca8210\n");

	pdata = spi_device->dev.platform_data;
	if (pdata) {
		if (pdata->extclockenable) {
			ca8210_unregister_ext_clock(spi_device);
			ca8210_config_extern_clk(pdata, spi_device, 0);
		}
		free_irq(pdata->irq_id, spi_device->dev.driver_data);
		kfree(pdata);
		spi_device->dev.platform_data = NULL;
	}
	/* get spi_device private data */
	priv = spi_get_drvdata(spi_device);
	if (priv) {
		dev_info(
			&spi_device->dev,
			"sync_down = %d, sync_up = %d\n",
			priv->sync_down,
			priv->sync_up
		);
		ca8210_dev_com_clear(spi_device->dev.driver_data);
		if (priv->hw) {
			if (priv->hw_registered)
				ieee802154_unregister_hw(priv->hw);
			ieee802154_free_hw(priv->hw);
			priv->hw = NULL;
			dev_info(
				&spi_device->dev,
				"Unregistered & freed ieee802154_hw.\n"
			);
		}
		if (IS_ENABLED(CONFIG_IEEE802154_CA8210_DEBUGFS))
			ca8210_test_interface_clear(priv);
	}

	return 0;
}

#define DRIVER_NAME "ca8210"

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}

struct mac_message {
	u8      command_id;
	u8      length;
	union {
		struct mcps_data_request_pset       data_req;
		struct mlme_set_request_pset        set_req;
		struct hwme_set_request_pset        hwme_set_req;
		struct hwme_get_request_pset        hwme_get_req;
		struct tdme_setsfr_request_pset     tdme_set_sfr_req;
		struct hwme_set_confirm_pset        hwme_set_cnf;
		struct hwme_get_confirm_pset        hwme_get_cnf;
		struct tdme_setsfr_confirm_pset     tdme_set_sfr_cnf;
		u8                                  u8param;
		u8                                  status;
		u8                                  payload[148];
	} pdata;
}


static u8 hwme_get_request_sync(
	u8           hw_attribute,
	u8          *hw_attribute_length,
	u8          *hw_attribute_value,
	void        *device_ref
)
{
	struct mac_message command, response;

	command.command_id = SPI_HWME_GET_REQUEST;
	command.length = 1;
	command.pdata.hwme_get_req.hw_attribute = hw_attribute;

	if (cascoda_api_downstream(
		&command.command_id,
		command.length + 2,
		&response.command_id,
		device_ref)) {
		return MAC_SYSTEM_ERROR;
	}

	if (response.command_id != SPI_HWME_GET_CONFIRM)
		return MAC_SYSTEM_ERROR;

	if (response.pdata.hwme_get_cnf.status == MAC_SUCCESS) {
		*hw_attribute_length =
			response.pdata.hwme_get_cnf.hw_attribute_length;
		memcpy(
			hw_attribute_value,
			response.pdata.hwme_get_cnf.hw_attribute_value,
			*hw_attribute_length
		);
	}

	return response.pdata.hwme_get_cnf.status;
}

ieee802154: ca8210: prevent memory leak

In ca8210_probe the allocated pdata needs to be assigned to
spi_device->dev.platform_data before calling ca8210_get_platform_data.
Othrwise when ca8210_get_platform_data fails pdata cannot be released.

Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Link: https://lore.kernel.org/r/20190917224713.26371-1-navid.emamdoost@gmail.com
Signed-off-by: Stefan Schmidt <stefan@datenfreihafen.org>

#define MAC_SUCCESS                     (0x00)

#define MAC_SYSTEM_ERROR                (0xFF)

#define SPI_HWME_GET_REQUEST           (HWME_GET_REQUEST + SPI_SYN)

#define SPI_HWME_GET_CONFIRM           (HWME_GET_CONFIRM + SPI_S2M + SPI_SYN)

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}


static void ca8210_mlme_reset_worker(struct work_struct *work)
{
	struct work_priv_container *wpc = container_of(
		work,
		struct work_priv_container,
		work
	);
	struct ca8210_priv *priv = wpc->priv;

	mlme_reset_request_sync(0, priv->spi);
	kfree(wpc);
}

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}

struct mac_message {
	u8      command_id;
	u8      length;
	union {
		struct mcps_data_request_pset       data_req;
		struct mlme_set_request_pset        set_req;
		struct hwme_set_request_pset        hwme_set_req;
		struct hwme_get_request_pset        hwme_get_req;
		struct tdme_setsfr_request_pset     tdme_set_sfr_req;
		struct hwme_set_confirm_pset        hwme_set_cnf;
		struct hwme_get_confirm_pset        hwme_get_cnf;
		struct tdme_setsfr_confirm_pset     tdme_set_sfr_cnf;
		u8                                  u8param;
		u8                                  status;
		u8                                  payload[148];
	} pdata;
}


static irqreturn_t ca8210_interrupt_handler(int irq, void *dev_id)
{
	struct ca8210_priv *priv = dev_id;
	int status;

	dev_dbg(&priv->spi->dev, "irq: Interrupt occurred\n");
	do {
		status = ca8210_spi_transfer(priv->spi, NULL, 0);
		if (status && (status != -EBUSY)) {
			dev_warn(
				&priv->spi->dev,
				"spi read failed, returned %d\n",
				status
			);
		}
	} while (status == -EBUSY);
	return IRQ_HANDLED;
}


static int ca8210_dev_com_init(struct ca8210_priv *priv)
{
	priv->mlme_workqueue = alloc_ordered_workqueue(
		"MLME work queue",
		WQ_UNBOUND
	);
	if (!priv->mlme_workqueue) {
		dev_crit(&priv->spi->dev, "alloc of mlme_workqueue failed!\n");
		return -ENOMEM;
	}

	priv->irq_workqueue = alloc_ordered_workqueue(
		"ca8210 irq worker",
		WQ_UNBOUND
	);
	if (!priv->irq_workqueue) {
		dev_crit(&priv->spi->dev, "alloc of irq_workqueue failed!\n");
		return -ENOMEM;
	}

	return 0;
}


static int ca8210_test_interface_init(struct ca8210_priv *priv)
{
	struct ca8210_test *test = &priv->test;
	char node_name[32];

	snprintf(
		node_name,
		sizeof(node_name),
		"ca8210@%d_%d",
		priv->spi->master->bus_num,
		priv->spi->chip_select
	);

	test->ca8210_dfs_spi_int = debugfs_create_file(
		node_name,
		0600, /* S_IRUSR | S_IWUSR */
		NULL,
		priv,
		&test_int_fops
	);
	if (IS_ERR(test->ca8210_dfs_spi_int)) {
		dev_err(
			&priv->spi->dev,
			"Error %ld when creating debugfs node\n",
			PTR_ERR(test->ca8210_dfs_spi_int)
		);
		return PTR_ERR(test->ca8210_dfs_spi_int);
	}
	debugfs_create_symlink("ca8210", NULL, node_name);
	init_waitqueue_head(&test->readq);
	return kfifo_alloc(
		&test->up_fifo,
		CA8210_TEST_INT_FIFO_SIZE,
		GFP_KERNEL
	);
}

#define DRIVER_NAME "ca8210"

#define CA8210_TEST_INT_FILE_NAME "ca8210_test"

#define CA8210_TEST_INT_FIFO_SIZE 256

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}


static void ca8210_stop(struct ieee802154_hw *hw)
{
}


static int ca8210_set_channel(
	struct ieee802154_hw  *hw,
	u8                     page,
	u8                     channel
)
{
	u8 status;
	struct ca8210_priv *priv = hw->priv;

	status = mlme_set_request_sync(
		PHY_CURRENT_CHANNEL,
		0,
		1,
		&channel,
		priv->spi
	);
	if (status) {
		dev_err(
			&priv->spi->dev,
			"error setting channel, MLME-SET.confirm status = %d\n",
			status
		);
	}
	return link_to_linux_err(status);
}

#define PHY_CURRENT_CHANNEL               (0x00)

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}

struct mac_message {
	u8      command_id;
	u8      length;
	union {
		struct mcps_data_request_pset       data_req;
		struct mlme_set_request_pset        set_req;
		struct hwme_set_request_pset        hwme_set_req;
		struct hwme_get_request_pset        hwme_get_req;
		struct tdme_setsfr_request_pset     tdme_set_sfr_req;
		struct hwme_set_confirm_pset        hwme_set_cnf;
		struct hwme_get_confirm_pset        hwme_get_cnf;
		struct tdme_setsfr_confirm_pset     tdme_set_sfr_cnf;
		u8                                  u8param;
		u8                                  status;
		u8                                  payload[148];
	} pdata;
}


static void ca8210_dev_com_clear(struct ca8210_priv *priv)
{
	flush_workqueue(priv->mlme_workqueue);
	destroy_workqueue(priv->mlme_workqueue);
	flush_workqueue(priv->irq_workqueue);
	destroy_workqueue(priv->irq_workqueue);
}


static int ca8210_set_hw_addr_filt(
	struct ieee802154_hw            *hw,
	struct ieee802154_hw_addr_filt  *filt,
	unsigned long                    changed
)
{
	u8 status = 0;
	struct ca8210_priv *priv = hw->priv;

	if (changed & IEEE802154_AFILT_PANID_CHANGED) {
		status = mlme_set_request_sync(
			MAC_PAN_ID,
			0,
			2,
			&filt->pan_id, priv->spi
		);
		if (status) {
			dev_err(
				&priv->spi->dev,
				"error setting pan id, MLME-SET.confirm status = %d",
				status
			);
			return link_to_linux_err(status);
		}
	}
	if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
		status = mlme_set_request_sync(
			MAC_SHORT_ADDRESS,
			0,
			2,
			&filt->short_addr, priv->spi
		);
		if (status) {
			dev_err(
				&priv->spi->dev,
				"error setting short address, MLME-SET.confirm status = %d",
				status
			);
			return link_to_linux_err(status);
		}
	}
	if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
		status = mlme_set_request_sync(
			NS_IEEE_ADDRESS,
			0,
			8,
			&filt->ieee_addr,
			priv->spi
		);
		if (status) {
			dev_err(
				&priv->spi->dev,
				"error setting ieee address, MLME-SET.confirm status = %d",
				status
			);
			return link_to_linux_err(status);
		}
	}
	/* TODO: Should use MLME_START to set coord bit? */
	return 0;
}

#define MAC_PAN_ID                        (0x50)

#define MAC_SHORT_ADDRESS                 (0x53)

#define NS_IEEE_ADDRESS                   (0xFF) /* Non-standard IEEE address */

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}


static void ca8210_hw_setup(struct ieee802154_hw *ca8210_hw)
{
	/* Support channels 11-26 */
	ca8210_hw->phy->supported.channels[0] = CA8210_VALID_CHANNELS;
	ca8210_hw->phy->supported.tx_powers_size = CA8210_MAX_TX_POWERS;
	ca8210_hw->phy->supported.tx_powers = ca8210_tx_powers;
	ca8210_hw->phy->supported.cca_ed_levels_size = CA8210_MAX_ED_LEVELS;
	ca8210_hw->phy->supported.cca_ed_levels = ca8210_ed_levels;
	ca8210_hw->phy->current_channel = 18;
	ca8210_hw->phy->current_page = 0;
	ca8210_hw->phy->transmit_power = 800;
	ca8210_hw->phy->cca.mode = NL802154_CCA_ENERGY_CARRIER;
	ca8210_hw->phy->cca.opt = NL802154_CCA_OPT_ENERGY_CARRIER_AND;
	ca8210_hw->phy->cca_ed_level = -9800;
	ca8210_hw->phy->symbol_duration = 16;
	ca8210_hw->phy->lifs_period = 40;
	ca8210_hw->phy->sifs_period = 12;
	ca8210_hw->flags =
		IEEE802154_HW_AFILT |
		IEEE802154_HW_OMIT_CKSUM |
		IEEE802154_HW_FRAME_RETRIES |
		IEEE802154_HW_PROMISCUOUS |
		IEEE802154_HW_CSMA_PARAMS;
	ca8210_hw->phy->flags =
		WPAN_PHY_FLAG_TXPOWER |
		WPAN_PHY_FLAG_CCA_ED_LEVEL |
		WPAN_PHY_FLAG_CCA_MODE;
}

#define CA8210_VALID_CHANNELS                 (0x07FFF800)

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}

struct hwme_set_confirm_pset {
	u8         status;
	u8         hw_attribute;
}

struct hwme_get_confirm_pset {
	u8         status;
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct tdme_setsfr_confirm_pset {
	u8         status;
	u8         sfr_page;
	u8         sfr_address;
}

struct mac_message {
	u8      command_id;
	u8      length;
	union {
		struct mcps_data_request_pset       data_req;
		struct mlme_set_request_pset        set_req;
		struct hwme_set_request_pset        hwme_set_req;
		struct hwme_get_request_pset        hwme_get_req;
		struct tdme_setsfr_request_pset     tdme_set_sfr_req;
		struct hwme_set_confirm_pset        hwme_set_cnf;
		struct hwme_get_confirm_pset        hwme_get_cnf;
		struct tdme_setsfr_confirm_pset     tdme_set_sfr_cnf;
		u8                                  u8param;
		u8                                  status;
		u8                                  payload[148];
	} pdata;
}


static ssize_t ca8210_test_int_user_write(
	struct file        *filp,
	const char __user  *in_buf,
	size_t              len,
	loff_t             *off
)
{
	int ret;
	struct ca8210_priv *priv = filp->private_data;
	u8 command[CA8210_SPI_BUF_SIZE];

	memset(command, SPI_IDLE, 6);
	if (len > CA8210_SPI_BUF_SIZE || len < 2) {
		dev_warn(
			&priv->spi->dev,
			"userspace requested erroneous write length (%zu)\n",
			len
		);
		return -EBADE;
	}

	ret = copy_from_user(command, in_buf, len);
	if (ret) {
		dev_err(
			&priv->spi->dev,
			"%d bytes could not be copied from userspace\n",
			ret
		);
		return -EIO;
	}
	if (len != command[1] + 2) {
		dev_err(
			&priv->spi->dev,
			"write len does not match packet length field\n"
		);
		return -EBADE;
	}

	ret = ca8210_test_check_upstream(command, priv->spi);
	if (ret == 0) {
		ret = ca8210_spi_exchange(
			command,
			command[1] + 2,
			NULL,
			priv->spi
		);
		if (ret < 0) {
			/* effectively 0 bytes were written successfully */
			dev_err(
				&priv->spi->dev,
				"spi exchange failed\n"
			);
			return ret;
		}
		if (command[0] & SPI_SYN)
			priv->sync_down++;
	}

	return len;
}

#define CA8210_SPI_BUF_SIZE 256

#define SPI_SYN                            (0x40)

#define SPI_IDLE                           (0xFF)

#define SPI_MLME_RESET_REQUEST         (MLME_RESET_REQUEST + SPI_SYN)

#define SPI_MLME_SET_REQUEST           (MLME_SET_REQUEST + SPI_SYN)

#define SPI_MLME_START_REQUEST         (MLME_START_REQUEST + SPI_SYN)

#define SPI_MLME_RESET_CONFIRM         (MLME_RESET_CONFIRM + SPI_S2M + SPI_SYN)

#define SPI_MLME_SET_CONFIRM           (MLME_SET_CONFIRM + SPI_S2M + SPI_SYN)

#define SPI_MLME_START_CONFIRM         (MLME_START_CONFIRM + SPI_S2M + SPI_SYN)

#define SPI_HWME_SET_REQUEST           (HWME_SET_REQUEST + SPI_SYN)

#define SPI_HWME_GET_REQUEST           (HWME_GET_REQUEST + SPI_SYN)

#define SPI_HWME_SET_CONFIRM           (HWME_SET_CONFIRM + SPI_S2M + SPI_SYN)

#define SPI_HWME_GET_CONFIRM           (HWME_GET_CONFIRM + SPI_S2M + SPI_SYN)

#define SPI_TDME_SETSFR_REQUEST        (TDME_SETSFR_REQUEST + SPI_SYN)

#define SPI_TDME_SET_REQUEST           (TDME_SET_REQUEST + SPI_SYN)

#define SPI_TDME_SETSFR_CONFIRM        (TDME_SETSFR_CONFIRM + SPI_S2M + SPI_SYN)

struct cas_control {
	struct spi_message msg;
	struct spi_transfer transfer;

	u8 tx_buf[CA8210_SPI_BUF_SIZE];
	u8 tx_in_buf[CA8210_SPI_BUF_SIZE];

	struct ca8210_priv *priv;
}

struct ca8210_test {
	struct dentry *ca8210_dfs_spi_int;
	struct kfifo up_fifo;
	wait_queue_head_t readq;
}

struct ca8210_priv {
	struct spi_device *spi;
	struct ieee802154_hw *hw;
	bool hw_registered;
	spinlock_t lock;
	struct workqueue_struct *mlme_workqueue;
	struct workqueue_struct *irq_workqueue;
	struct sk_buff *tx_skb;
	u8 nextmsduhandle;
	struct clk *clk;
	int last_dsn;
	struct ca8210_test test;
	bool async_tx_pending;
	u8 *sync_command_response;
	struct completion ca8210_is_awake;
	int sync_down, sync_up;
	struct completion spi_transfer_complete, sync_exchange_complete;
	bool promiscuous;
	int retries;
}

struct work_priv_container {
	struct work_struct work;
	struct ca8210_priv *priv;
}

struct ca8210_platform_data {
	bool extclockenable;
	unsigned int extclockfreq;
	unsigned int extclockgpio;
	int gpio_reset;
	int gpio_irq;
	int irq_id;
}

struct fulladdr {
	u8         mode;
	u8         pan_id[2];
	u8         address[8];
}

struct secspec {
	u8         security_level;
	u8         key_id_mode;
	u8         key_source[8];
	u8         key_index;
}

struct mcps_data_request_pset {
	u8              src_addr_mode;
	struct fulladdr dst;
	u8              msdu_length;
	u8              msdu_handle;
	u8              tx_options;
	u8              msdu[MAX_DATA_SIZE];
}

struct mlme_set_request_pset {
	u8         pib_attribute;
	u8         pib_attribute_index;
	u8         pib_attribute_length;
	u8         pib_attribute_value[MAX_ATTRIBUTE_SIZE];
}

struct hwme_set_request_pset {
	u8         hw_attribute;
	u8         hw_attribute_length;
	u8         hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE];
}

struct hwme_get_request_pset {
	u8         hw_attribute;
}

struct tdme_setsfr_request_pset {
	u8         sfr_page;
	u8         sfr_address;
	u8         sfr_value;
}


static void boxes_check(size_t b,size_t m)
{
    if ( b > m ) {
#ifdef EXIV2_DEBUG_MESSAGES
        std::cout << "Exiv2::Jp2Image::readMetadata box maximum exceeded" << std::endl;
#endif
        throw Error(kerCorruptedMetadata);
    }
}

Better bounds checking in Jp2Image::encodeJp2Header()

#define     JP2_JP   0x6a502020    /**< JPEG 2000 signature box */

#define     JP2_FTYP 0x66747970    /**< File type box */

#define     JP2_JP2H 0x6a703268    /**< JP2 header box (super-box) */

#define     JP2_IHDR 0x69686472    /**< Image header box */

#define     JP2_COLR 0x636f6c72    /**< Colour specification box */

#define     JP2_JP2C 0x6a703263    /**< Contiguous codestream box */

#define     JP2_URL  0x75726c20    /**< Data entry URL box */

#define     JP2_PCLR 0x70636c72    /**< Palette box */

#define     JP2_CMAP 0x636d6170    /**< Component Mapping box */

#define     JP2_CDEF 0x63646566    /**< Channel Definition box */

#define     JP2_DTBL 0x6474626c    /**< Data Reference box */

#define     JP2_BPCC 0x62706363    /**< Bits per component box */


    void Jp2Image::encodeJp2Header(const DataBuf& boxBuf,DataBuf& outBuf)
    {
        DataBuf output(boxBuf.size_ + iccProfile_.size_ + 100); // allocate sufficient space
        long    outlen = sizeof(Jp2BoxHeader) ; // now many bytes have we written to output?
        long    inlen = sizeof(Jp2BoxHeader) ; // how many bytes have we read from boxBuf?
        Jp2BoxHeader* pBox   = (Jp2BoxHeader*) boxBuf.pData_;
        uint32_t      length = getLong((byte*)&pBox->length, bigEndian);
        uint32_t      count  = sizeof (Jp2BoxHeader);
        char*         p      = (char*) boxBuf.pData_;
        bool          bWroteColor = false ;

        while ( count < length || !bWroteColor ) {
            Jp2BoxHeader* pSubBox = (Jp2BoxHeader*) (p+count) ;

            // copy data.  pointer could be into a memory mapped file which we will decode!
            Jp2BoxHeader   subBox ; memcpy(&subBox,pSubBox,sizeof(subBox));
            Jp2BoxHeader   newBox =  subBox;

            if ( count < length ) {
                subBox.length = getLong((byte*)&subBox.length, bigEndian);
                subBox.type   = getLong((byte*)&subBox.type  , bigEndian);
#ifdef EXIV2_DEBUG_MESSAGES
                std::cout << "Jp2Image::encodeJp2Header subbox: "<< toAscii(subBox.type) << " length = " << subBox.length << std::endl;
#endif
                enforce(subBox.length <= length - count, Exiv2::kerCorruptedMetadata);
                count        += subBox.length;
                newBox.type   = subBox.type;
            } else {
                subBox.length=0;
                newBox.type = kJp2BoxTypeColorHeader;
                count = length;
            }

            uint32_t newlen = subBox.length;
            if ( newBox.type == kJp2BoxTypeColorHeader ) {
                bWroteColor = true ;
                if ( ! iccProfileDefined() ) {
                    const char* pad   = "\x01\x00\x00\x00\x00\x00\x10\x00\x00\x05\x1cuuid";
                    uint32_t    psize = 15;
                    newlen            = sizeof(newBox) + psize ;
                    enforce(newlen <= output.size_ - outlen, Exiv2::kerCorruptedMetadata);
                    ul2Data((byte*)&newBox.length,psize      ,bigEndian);
                    ul2Data((byte*)&newBox.type  ,newBox.type,bigEndian);
                    ::memcpy(output.pData_+outlen                     ,&newBox            ,sizeof(newBox));
                    ::memcpy(output.pData_+outlen+sizeof(newBox)      ,pad                ,psize         );
                } else {
                    const char* pad   = "\x02\x00\x00";
                    uint32_t    psize = 3;
                    newlen            = sizeof(newBox) + psize + iccProfile_.size_;
                    enforce(newlen <= output.size_ - outlen, Exiv2::kerCorruptedMetadata);
                    ul2Data((byte*)&newBox.length,newlen,bigEndian);
                    ul2Data((byte*)&newBox.type,newBox.type,bigEndian);
                    ::memcpy(output.pData_+outlen                     ,&newBox            ,sizeof(newBox)  );
                    ::memcpy(output.pData_+outlen+sizeof(newBox)      , pad               ,psize           );
                    ::memcpy(output.pData_+outlen+sizeof(newBox)+psize,iccProfile_.pData_,iccProfile_.size_);
                }
            } else {
                enforce(newlen <= output.size_ - outlen, Exiv2::kerCorruptedMetadata);
                ::memcpy(output.pData_+outlen,boxBuf.pData_+inlen,subBox.length);
            }

            outlen += newlen;
            inlen  += subBox.length;
        }

        // allocate the correct number of bytes, copy the data and update the box header
        outBuf.alloc(outlen);
        ::memcpy(outBuf.pData_,output.pData_,outlen);
        pBox   = (Jp2BoxHeader*) outBuf.pData_;
        ul2Data((byte*)&pBox->type,kJp2BoxTypeJp2Header,bigEndian);
        ul2Data((byte*)&pBox->length,outlen,bigEndian);
    } // Jp2Image::encodeJp2Header

const uint32_t kJp2BoxTypeJp2Header   = 0x6a703268;

const uint32_t kJp2BoxTypeImageHeader = 0x69686472;

const uint32_t kJp2BoxTypeColorHeader = 0x636f6c72;

const uint32_t kJp2BoxTypeUuid        = 0x75756964;

const uint32_t kJp2BoxTypeClose       = 0x6a703263;

#define     JP2_JP   0x6a502020    /**< JPEG 2000 signature box */

#define     JP2_FTYP 0x66747970    /**< File type box */

#define     JP2_JP2H 0x6a703268    /**< JP2 header box (super-box) */

#define     JP2_IHDR 0x69686472    /**< Image header box */

#define     JP2_COLR 0x636f6c72    /**< Colour specification box */

#define     JP2_JP2C 0x6a703263    /**< Contiguous codestream box */

#define     JP2_URL  0x75726c20    /**< Data entry URL box */

#define     JP2_PCLR 0x70636c72    /**< Palette box */

#define     JP2_CMAP 0x636d6170    /**< Component Mapping box */

#define     JP2_CDEF 0x63646566    /**< Channel Definition box */

#define     JP2_DTBL 0x6474626c    /**< Data Reference box */

#define     JP2_BPCC 0x62706363    /**< Bits per component box */

#define     JP2_JP2  0x6a703220    /**< File type fields */

const unsigned char kJp2UuidExif[] = "JpgTiffExif->JP2";

const unsigned char kJp2UuidIptc[] = "\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38";

const unsigned char kJp2UuidXmp[]  = "\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac";

const unsigned char Jp2Signature[12] = { 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a };


    void Jp2Image::printStructure(std::ostream& out, PrintStructureOption option, int depth)
    {
        if (io_->open() != 0)
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());

        // Ensure that this is the correct image type
        if (!isJp2Type(*io_, false)) {
            if (io_->error() || io_->eof())
                throw Error(kerFailedToReadImageData);
            throw Error(kerNotAJpeg);
        }

        bool bPrint = option == kpsBasic || option == kpsRecursive;
        bool bRecursive = option == kpsRecursive;
        bool bICC = option == kpsIccProfile;
        bool bXMP = option == kpsXMP;
        bool bIPTCErase = option == kpsIptcErase;

        if (bPrint) {
            out << "STRUCTURE OF JPEG2000 FILE: " << io_->path() << std::endl;
            out << " address |   length | box       | data" << std::endl;
        }

        if ( bPrint || bXMP || bICC || bIPTCErase ) {

            long              position  = 0;
            Jp2BoxHeader      box       = {1,1};
            Jp2BoxHeader      subBox    = {1,1};
            Jp2UuidBox        uuid      = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
            bool              bLF       = false;

            while (box.length && box.type != kJp2BoxTypeClose && io_->read((byte*)&box, sizeof(box)) == sizeof(box))
            {
                position   = io_->tell();
                box.length = getLong((byte*)&box.length, bigEndian);
                box.type = getLong((byte*)&box.type, bigEndian);
                enforce(box.length <= io_->size()-io_->tell() , Exiv2::kerCorruptedMetadata);

                if (bPrint) {
                    out << Internal::stringFormat("%8ld | %8ld | ", position - sizeof(box),
                                                  (size_t)box.length)
                        << toAscii(box.type) << "      | ";
                    bLF = true;
                    if (box.type == kJp2BoxTypeClose)
                        lf(out, bLF);
                }
                if (box.type == kJp2BoxTypeClose)
                    break;

                switch (box.type) {
                    case kJp2BoxTypeJp2Header: {
                        lf(out, bLF);

                        while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox) &&
                               io_->tell() < position + (long)box.length)  // don't read beyond the box!
                        {
                            int address = io_->tell() - sizeof(subBox);
                            subBox.length = getLong((byte*)&subBox.length, bigEndian);
                            subBox.type = getLong((byte*)&subBox.type, bigEndian);

                            if (subBox.length < sizeof(box) || subBox.length > io_->size() - io_->tell()) {
                                throw Error(kerCorruptedMetadata);
                            }

                            DataBuf data(subBox.length - sizeof(box));
                            io_->read(data.pData_, data.size_);
                            if (bPrint) {
                                out << Internal::stringFormat("%8ld | %8ld |  sub:", (size_t)address,
                                                              (size_t)subBox.length)
                                    << toAscii(subBox.type) << " | "
                                    << Internal::binaryToString(makeSlice(data, 0, std::min(30l, data.size_)));
                                bLF = true;
                            }

                            if (subBox.type == kJp2BoxTypeColorHeader) {
                                long pad = 3;  // don't know why there are 3 padding bytes
                                if (bPrint) {
                                    out << " | pad:";
                                    for (int i = 0; i < 3; i++)
                                        out << " " << (int)data.pData_[i];
                                }
                                long iccLength = getULong(data.pData_ + pad, bigEndian);
                                if (bPrint) {
                                    out << " | iccLength:" << iccLength;
                                }
                                if (bICC) {
                                    out.write((const char*)data.pData_ + pad, iccLength);
                                }
                            }
                            lf(out, bLF);
                        }
                    } break;

                    case kJp2BoxTypeUuid: {
                        if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid)) {
                            bool bIsExif = memcmp(uuid.uuid, kJp2UuidExif, sizeof(uuid)) == 0;
                            bool bIsIPTC = memcmp(uuid.uuid, kJp2UuidIptc, sizeof(uuid)) == 0;
                            bool bIsXMP = memcmp(uuid.uuid, kJp2UuidXmp, sizeof(uuid)) == 0;

                            bool bUnknown = !(bIsExif || bIsIPTC || bIsXMP);

                            if (bPrint) {
                                if (bIsExif)
                                    out << "Exif: ";
                                if (bIsIPTC)
                                    out << "IPTC: ";
                                if (bIsXMP)
                                    out << "XMP : ";
                                if (bUnknown)
                                    out << "????: ";
                            }

                            DataBuf rawData;
                            rawData.alloc(box.length - sizeof(uuid) - sizeof(box));
                            long bufRead = io_->read(rawData.pData_, rawData.size_);
                            if (io_->error())
                                throw Error(kerFailedToReadImageData);
                            if (bufRead != rawData.size_)
                                throw Error(kerInputDataReadFailed);

                            if (bPrint) {
                                out << Internal::binaryToString(
                                        makeSlice(rawData, 0, rawData.size_>40?40:rawData.size_));
                                out.flush();
                            }
                            lf(out, bLF);

                            if (bIsExif && bRecursive && rawData.size_ > 8) { // "II*\0long"
                                if ((rawData.pData_[0] == rawData.pData_[1]) &&
                                    (rawData.pData_[0] == 'I' || rawData.pData_[0] == 'M')) {
                                    BasicIo::UniquePtr p = BasicIo::UniquePtr(new MemIo(rawData.pData_, rawData.size_));
                                    printTiffStructure(*p, out, option, depth);
                                }
                            }

                            if (bIsIPTC && bRecursive) {
                                IptcData::printStructure(out, makeSlice(rawData.pData_, 0, rawData.size_), depth);
                            }

                            if (bIsXMP && bXMP) {
                                out.write((const char*)rawData.pData_, rawData.size_);
                            }
                        }
                    } break;

                    default:
                        break;
                }

                // Move to the next box.
                io_->seek(static_cast<long>(position - sizeof(box) + box.length), BasicIo::beg);
                if (io_->error())
                    throw Error(kerFailedToReadImageData);
                if (bPrint)
                    lf(out, bLF);
            }
        }
    }  // JpegBase::printStructure


    void Jp2Image::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(kerInputDataReadFailed);
        if (!outIo.isopen()) throw Error(kerImageWriteFailed);

#ifdef EXIV2_DEBUG_MESSAGES
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << std::endl;
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << std::endl;
#endif

        // Ensure that this is the correct image type
        if (!isJp2Type(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(kerInputDataReadFailed);
            throw Error(kerNoImageInInputData);
        }

        // Write JPEG2000 Signature.
        if (outIo.write(Jp2Signature, 12) != 12) throw Error(kerImageWriteFailed);

        Jp2BoxHeader box = {0,0};

        byte    boxDataSize[4];
        byte    boxUUIDtype[4];
        DataBuf bheaderBuf(8);     // Box header : 4 bytes (data size) + 4 bytes (box type).

        // FIXME: Andreas, why the loop do not stop when EOF is taken from _io. The loop go out by an exception
        // generated by a zero size data read.

        while(io_->tell() < (long) io_->size())
        {
#ifdef EXIV2_DEBUG_MESSAGES
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << std::endl;
#endif

            // Read chunk header.

            std::memset(bheaderBuf.pData_, 0x00, bheaderBuf.size_);
            long bufRead = io_->read(bheaderBuf.pData_, bheaderBuf.size_);
            if (io_->error()) throw Error(kerFailedToReadImageData);
            if (bufRead != bheaderBuf.size_) throw Error(kerInputDataReadFailed);

            // Decode box header.

            box.length = getLong(bheaderBuf.pData_,     bigEndian);
            box.type   = getLong(bheaderBuf.pData_ + 4, bigEndian);

#ifdef EXIV2_DEBUG_MESSAGES
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: box type: " << toAscii(box.type)
                      << " length: " << box.length << std::endl;
#endif

            if (box.length == 0)
            {
#ifdef EXIV2_DEBUG_MESSAGES
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. "
                             "This is the last box of file." << std::endl;
#endif
                box.length = (uint32_t) (io_->size() - io_->tell() + 8);
            }
            if (box.length < 8)
            {
                // box is broken, so there is nothing we can do here
                throw Error(kerCorruptedMetadata);
            }

            // Read whole box : Box header + Box data (not fixed size - can be null).

            DataBuf boxBuf(box.length);                             // Box header (8 bytes) + box data.
            memcpy(boxBuf.pData_, bheaderBuf.pData_, 8);               // Copy header.
            bufRead = io_->read(boxBuf.pData_ + 8, box.length - 8); // Extract box data.
            if (io_->error())
            {
#ifdef EXIV2_DEBUG_MESSAGES
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file" << std::endl;
#endif

                throw Error(kerFailedToReadImageData);
            }

            if (bufRead != (long)(box.length - 8))
            {
#ifdef EXIV2_DEBUG_MESSAGES
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data" << std::endl;
#endif
                throw Error(kerInputDataReadFailed);
            }

            switch(box.type)
            {
                case kJp2BoxTypeJp2Header:
                {
                    DataBuf newBuf;
                    encodeJp2Header(boxBuf,newBuf);
#ifdef EXIV2_DEBUG_MESSAGES
                    std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (length: " << box.length << ")" << std::endl;
#endif
                    if (outIo.write(newBuf.pData_, newBuf.size_) != newBuf.size_) throw Error(kerImageWriteFailed);

                    // Write all updated metadata here, just after JP2Header.

                    if (exifData_.count() > 0)
                    {
                        // Update Exif data to a new UUID box

                        Blob blob;
                        ExifParser::encode(blob, littleEndian, exifData_);
                        if (blob.size())
                        {
                            DataBuf rawExif(static_cast<long>(blob.size()));
                            memcpy(rawExif.pData_, &blob[0], blob.size());

                            DataBuf boxData(8 + 16 + rawExif.size_);
                            ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                            ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                            memcpy(boxData.pData_,          boxDataSize,    4);
                            memcpy(boxData.pData_ + 4,      boxUUIDtype,    4);
                            memcpy(boxData.pData_ + 8,      kJp2UuidExif,   16);
                            memcpy(boxData.pData_ + 8 + 16, rawExif.pData_, rawExif.size_);

#ifdef EXIV2_DEBUG_MESSAGES
                            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (length: "
                                      << boxData.size_ << std::endl;
#endif
                            if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
                        }
                    }

                    if (iptcData_.count() > 0)
                    {
                        // Update Iptc data to a new UUID box

                        DataBuf rawIptc = IptcParser::encode(iptcData_);
                        if (rawIptc.size_ > 0)
                        {
                            DataBuf boxData(8 + 16 + rawIptc.size_);
                            ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                            ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                            memcpy(boxData.pData_,          boxDataSize,    4);
                            memcpy(boxData.pData_ + 4,      boxUUIDtype,    4);
                            memcpy(boxData.pData_ + 8,      kJp2UuidIptc,   16);
                            memcpy(boxData.pData_ + 8 + 16, rawIptc.pData_, rawIptc.size_);

#ifdef EXIV2_DEBUG_MESSAGES
                            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (length: "
                                      << boxData.size_ << std::endl;
#endif
                            if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
                        }
                    }

                    if (writeXmpFromPacket() == false)
                    {
                        if (XmpParser::encode(xmpPacket_, xmpData_) > 1)
                        {
#ifndef SUPPRESS_WARNINGS
                            EXV_ERROR << "Failed to encode XMP metadata." << std::endl;
#endif
                        }
                    }
                    if (xmpPacket_.size() > 0)
                    {
                        // Update Xmp data to a new UUID box

                        DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size()));
                        DataBuf boxData(8 + 16 + xmp.size_);
                        ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                        ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                        memcpy(boxData.pData_,          boxDataSize,  4);
                        memcpy(boxData.pData_ + 4,      boxUUIDtype,  4);
                        memcpy(boxData.pData_ + 8,      kJp2UuidXmp,  16);
                        memcpy(boxData.pData_ + 8 + 16, xmp.pData_,   xmp.size_);

#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (length: "
                                  << boxData.size_ << ")" << std::endl;
#endif
                        if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
                    }

                    break;
                }

                case kJp2BoxTypeUuid:
                {
                    if(memcmp(boxBuf.pData_ + 8, kJp2UuidExif, 16) == 0)
                    {
#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box" << std::endl;
#endif
                    }
                    else if(memcmp(boxBuf.pData_ + 8, kJp2UuidIptc, 16) == 0)
                    {
#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box" << std::endl;
#endif
                    }
                    else if(memcmp(boxBuf.pData_ + 8, kJp2UuidXmp,  16) == 0)
                    {
#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box" << std::endl;
#endif
                    }
                    else
                    {
#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (length: " << box.length << ")" << std::endl;
#endif
                        if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(kerImageWriteFailed);
                    }
                    break;
                }

                default:
                {
#ifdef EXIV2_DEBUG_MESSAGES
                    std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (length: " << box.length << ")" << std::endl;
#endif
                    if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(kerImageWriteFailed);

                    break;
                }
            }
        }

#ifdef EXIV2_DEBUG_MESSAGES
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF" << std::endl;
#endif

    } // Jp2Image::doWriteMetadata


    void Jp2Image::readMetadata()
    {
#ifdef EXIV2_DEBUG_MESSAGES
        std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << std::endl;
#endif
        if (io_->open() != 0)
        {
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());
        }
        IoCloser closer(*io_);
        // Ensure that this is the correct image type
        if (!isJp2Type(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
            throw Error(kerNotAnImage, "JPEG-2000");
        }

        long              position  = 0;
        Jp2BoxHeader      box       = {0,0};
        Jp2BoxHeader      subBox    = {0,0};
        Jp2ImageHeaderBox ihdr      = {0,0,0,0,0,0,0,0};
        Jp2UuidBox        uuid      = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
        size_t            boxes     = 0 ;
        size_t            boxem     = 1000 ; // boxes max

        while (io_->read((byte*)&box, sizeof(box)) == sizeof(box))
        {
            boxes_check(boxes++,boxem );
            position   = io_->tell();
            box.length = getLong((byte*)&box.length, bigEndian);
            box.type   = getLong((byte*)&box.type, bigEndian);
#ifdef EXIV2_DEBUG_MESSAGES
            std::cout << "Exiv2::Jp2Image::readMetadata: "
                      << "Position: " << position
                      << " box type: " << toAscii(box.type)
                      << " length: " << box.length
                      << std::endl;
#endif

            if (box.length == 0) return ;

            if (box.length == 1)
            {
                // FIXME. Special case. the real box size is given in another place.
            }

            switch(box.type)
            {
                case kJp2BoxTypeJp2Header:
                {
#ifdef EXIV2_DEBUG_MESSAGES
                    std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found" << std::endl;
#endif
                    long restore = io_->tell();

                    while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox) && subBox.length )
                    {
                        boxes_check(boxes++, boxem) ;
                        subBox.length = getLong((byte*)&subBox.length, bigEndian);
                        subBox.type   = getLong((byte*)&subBox.type, bigEndian);
                        if (subBox.length > io_->size() ) {
                            throw Error(kerCorruptedMetadata);
                        }
#ifdef EXIV2_DEBUG_MESSAGES
                        std::cout << "Exiv2::Jp2Image::readMetadata: "
                        << "subBox = " << toAscii(subBox.type) << " length = " << subBox.length << std::endl;
#endif
                        if(subBox.type == kJp2BoxTypeColorHeader && subBox.length != 15)
                        {
#ifdef EXIV2_DEBUG_MESSAGES
                            std::cout << "Exiv2::Jp2Image::readMetadata: "
                                     << "Color data found" << std::endl;
#endif

                            const long pad = 3 ; // 3 padding bytes 2 0 0
                            const size_t data_length = Safe::add(subBox.length, static_cast<uint32_t>(8));
                            // data_length makes no sense if it is larger than the rest of the file
                            if (data_length > io_->size() - io_->tell()) {
                                throw Error(kerCorruptedMetadata);
                            }
                            DataBuf data(static_cast<long>(data_length));
                            io_->read(data.pData_,data.size_);
                            const long    iccLength = getULong(data.pData_+pad, bigEndian);
                            // subtracting pad from data.size_ is safe:
                            // size_ is at least 8 and pad = 3
                            if (iccLength > data.size_ - pad) {
                                throw Error(kerCorruptedMetadata);
                            }
                            DataBuf icc(iccLength);
                            ::memcpy(icc.pData_,data.pData_+pad,icc.size_);
#ifdef EXIV2_DEBUG_MESSAGES
                            const char* iccPath = "/tmp/libexiv2_jp2.icc";
                            FILE* f = fopen(iccPath,"wb");
                            if ( f ) {
                                fwrite(icc.pData_,icc.size_,1,f);
                                fclose(f);
                            }
                            std::cout << "Exiv2::Jp2Image::readMetadata: wrote iccProfile " << icc.size_<< " bytes to " << iccPath << std::endl ;
#endif
                            setIccProfile(icc);
                        }

                        if( subBox.type == kJp2BoxTypeImageHeader)
                        {
                            io_->read((byte*)&ihdr, sizeof(ihdr));
#ifdef EXIV2_DEBUG_MESSAGES
                            std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found" << std::endl;
#endif
                            ihdr.imageHeight            = getLong((byte*)&ihdr.imageHeight, bigEndian);
                            ihdr.imageWidth             = getLong((byte*)&ihdr.imageWidth, bigEndian);
                            ihdr.componentCount         = getShort((byte*)&ihdr.componentCount, bigEndian);
                            ihdr.compressionTypeProfile = getShort((byte*)&ihdr.compressionTypeProfile, bigEndian);

                            pixelWidth_  = ihdr.imageWidth;
                            pixelHeight_ = ihdr.imageHeight;
                        }

                        io_->seek(restore,BasicIo::beg);
                        if ( io_->seek(subBox.length, Exiv2::BasicIo::cur) != 0 ) {
                            throw Error(kerCorruptedMetadata);
                        }
                        restore = io_->tell();
                    }
                    break;
                }

                case kJp2BoxTypeUuid:
                {
#ifdef EXIV2_DEBUG_MESSAGES
                    std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found" << std::endl;
#endif

                    if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid))
                    {
                        DataBuf rawData;
                        long    bufRead;
                        bool    bIsExif = memcmp(uuid.uuid, kJp2UuidExif, sizeof(uuid))==0;
                        bool    bIsIPTC = memcmp(uuid.uuid, kJp2UuidIptc, sizeof(uuid))==0;
                        bool    bIsXMP  = memcmp(uuid.uuid, kJp2UuidXmp , sizeof(uuid))==0;

                        if(bIsExif)
                        {
#ifdef EXIV2_DEBUG_MESSAGES
                           std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found" << std::endl ;
#endif
                            rawData.alloc(box.length - (sizeof(box) + sizeof(uuid)));
                            bufRead = io_->read(rawData.pData_, rawData.size_);
                            if (io_->error()) throw Error(kerFailedToReadImageData);
                            if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);

                            if (rawData.size_ > 8) // "II*\0long"
                            {
                                // Find the position of Exif header in bytes array.
                                long pos = (     (rawData.pData_[0]      == rawData.pData_[1])
                                           &&    (rawData.pData_[0]=='I' || rawData.pData_[0]=='M')
                                           )  ? 0 : -1;

                                // #1242  Forgive having Exif\0\0 in rawData.pData_
                                const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
                                for (long i=0 ; pos < 0 && i < rawData.size_-(long)sizeof(exifHeader) ; i++)
                                {
                                    if (memcmp(exifHeader, &rawData.pData_[i], sizeof(exifHeader)) == 0)
                                    {
                                        pos = i+sizeof(exifHeader);
#ifndef SUPPRESS_WARNINGS
                                        EXV_WARNING << "Reading non-standard UUID-EXIF_bad box in " << io_->path() << std::endl;
#endif

                                    }
                                }

                                // If found it, store only these data at from this place.
                                if (pos >= 0 )
                                {
#ifdef EXIV2_DEBUG_MESSAGES
                                    std::cout << "Exiv2::Jp2Image::readMetadata: Exif header found at position " << pos << std::endl;
#endif
                                    ByteOrder bo = TiffParser::decode(exifData(),
                                                                      iptcData(),
                                                                      xmpData(),
                                                                      rawData.pData_ + pos,
                                                                      rawData.size_ - pos);
                                    setByteOrder(bo);
                                }
                            }
                            else
                            {
#ifndef SUPPRESS_WARNINGS
                                EXV_WARNING << "Failed to decode Exif metadata." << std::endl;
#endif
                                exifData_.clear();
                            }
                        }

                        if(bIsIPTC)
                        {
#ifdef EXIV2_DEBUG_MESSAGES
                           std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found" << std::endl;
#endif
                            rawData.alloc(box.length - (sizeof(box) + sizeof(uuid)));
                            bufRead = io_->read(rawData.pData_, rawData.size_);
                            if (io_->error()) throw Error(kerFailedToReadImageData);
                            if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);

                            if (IptcParser::decode(iptcData_, rawData.pData_, rawData.size_))
                            {
#ifndef SUPPRESS_WARNINGS
                                EXV_WARNING << "Failed to decode IPTC metadata." << std::endl;
#endif
                                iptcData_.clear();
                            }
                        }

                        if(bIsXMP)
                        {
#ifdef EXIV2_DEBUG_MESSAGES
                           std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found" << std::endl;
#endif
                            rawData.alloc(box.length - (uint32_t)(sizeof(box) + sizeof(uuid)));
                            bufRead = io_->read(rawData.pData_, rawData.size_);
                            if (io_->error()) throw Error(kerFailedToReadImageData);
                            if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);
                            xmpPacket_.assign(reinterpret_cast<char *>(rawData.pData_), rawData.size_);

                            std::string::size_type idx = xmpPacket_.find_first_of('<');
                            if (idx != std::string::npos && idx > 0)
                            {
#ifndef SUPPRESS_WARNINGS
                                EXV_WARNING << "Removing " << static_cast<uint32_t>(idx)
                                            << " characters from the beginning of the XMP packet" << std::endl;
#endif
                                xmpPacket_ = xmpPacket_.substr(idx);
                            }

                            if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_))
                            {
#ifndef SUPPRESS_WARNINGS
                                EXV_WARNING << "Failed to decode XMP metadata." << std::endl;
#endif
                            }
                        }
                    }
                    break;
                }

                default:
                {
                    break;
                }
            }

            // Move to the next box.
            io_->seek(static_cast<long>(position - sizeof(box) + box.length), BasicIo::beg);
            if (io_->error()) throw Error(kerFailedToReadImageData);
        }

    } // Jp2Image::readMetadata


    Jp2Image::Jp2Image(BasicIo::UniquePtr io, bool create)
            : Image(ImageType::jp2, mdExif | mdIptc | mdXmp, std::move(io))
    {
        if (create)
        {
            if (io_->open() == 0)
            {
#ifdef EXIV2_DEBUG_MESSAGES
                std::cerr << "Exiv2::Jp2Image:: Creating JPEG2000 image to memory" << std::endl;
#endif
                IoCloser closer(*io_);
                if (io_->write(Jp2Blank, sizeof(Jp2Blank)) != sizeof(Jp2Blank))
                {
#ifdef EXIV2_DEBUG_MESSAGES
                    std::cerr << "Exiv2::Jp2Image:: Failed to create JPEG2000 image on memory" << std::endl;
#endif
                }
            }
        }
    } // Jp2Image::Jp2Image

#define     JP2_IHDR 0x69686472    /**< Image header box */


exif_mnote_data_canon_load (ExifMnoteData *ne,
	const unsigned char *buf, unsigned int buf_size)
{
	ExifMnoteDataCanon *n = (ExifMnoteDataCanon *) ne;
	ExifShort c;
	size_t i, tcount, o, datao;
	long failsafe_size = 0;

	if (!n || !buf || !buf_size) {
		exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
			  "ExifMnoteCanon", "Short MakerNote");
		return;
	}
	datao = 6 + n->offset;
	if (CHECKOVERFLOW(datao, buf_size, 2)) {
		exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
			  "ExifMnoteCanon", "Short MakerNote");
		return;
	}

	/* Read the number of tags */
	c = exif_get_short (buf + datao, n->order);
	datao += 2;

	/* Remove any old entries */
	exif_mnote_data_canon_clear (n);

	/* Reserve enough space for all the possible MakerNote tags */
	n->entries = exif_mem_alloc (ne->mem, sizeof (MnoteCanonEntry) * c);
	if (!n->entries) {
		EXIF_LOG_NO_MEMORY(ne->log, "ExifMnoteCanon", sizeof (MnoteCanonEntry) * c);
		return;
	}

	/* Parse the entries */
	tcount = 0;
	for (i = c, o = datao; i; --i, o += 12) {
		size_t s;

		memset(&n->entries[tcount], 0, sizeof(MnoteCanonEntry));
		if (CHECKOVERFLOW(o,buf_size,12)) {
			exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
				"ExifMnoteCanon", "Short MakerNote");
			break;
		}

		n->entries[tcount].tag        = exif_get_short (buf + o, n->order);
		n->entries[tcount].format     = exif_get_short (buf + o + 2, n->order);
		n->entries[tcount].components = exif_get_long (buf + o + 4, n->order);
		n->entries[tcount].order      = n->order;

		exif_log (ne->log, EXIF_LOG_CODE_DEBUG, "ExifMnoteCanon",
			"Loading entry 0x%x ('%s')...", n->entries[tcount].tag,
			 mnote_canon_tag_get_name (n->entries[tcount].tag));

		/* Check if we overflow the multiplication. Use buf_size as the max size for integer overflow detection,
		 * we will check the buffer sizes closer later. */
		if (	exif_format_get_size (n->entries[tcount].format) &&
			buf_size / exif_format_get_size (n->entries[tcount].format) < n->entries[tcount].components
		) {
			exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
				  "ExifMnoteCanon", "Tag size overflow detected (%u * %lu)", exif_format_get_size (n->entries[tcount].format), n->entries[tcount].components);
			continue;
		}

		/*
		 * Size? If bigger than 4 bytes, the actual data is not
		 * in the entry but somewhere else (offset).
		 */
		s = exif_format_get_size (n->entries[tcount].format) *
								  n->entries[tcount].components;
		n->entries[tcount].size = s;
		if (!s) {
			exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
				  "ExifMnoteCanon",
				  "Invalid zero-length tag size");
			continue;

		} else {
			size_t dataofs = o + 8;
			if (s > 4) dataofs = exif_get_long (buf + dataofs, n->order) + 6;

			if (CHECKOVERFLOW(dataofs, buf_size, s)) {
				exif_log (ne->log, EXIF_LOG_CODE_DEBUG,
					"ExifMnoteCanon",
					"Tag data past end of buffer (%u > %u)",
					(unsigned)(dataofs + s), buf_size);
				continue;
			}

			n->entries[tcount].data = exif_mem_alloc (ne->mem, s);
			if (!n->entries[tcount].data) {
				EXIF_LOG_NO_MEMORY(ne->log, "ExifMnoteCanon", s);
				continue;
			}
			memcpy (n->entries[tcount].data, buf + dataofs, s);
		}

		/* Track the size of decoded tag data. A malicious file could
		 * be crafted to cause extremely large values here without
		 * tripping any buffer range checks.  This is especially bad
		 * with the libexif representation of Canon MakerNotes because
		 * some arrays are turned into individual tags that the
		 * application must loop around. */
		failsafe_size += mnote_canon_entry_count_values(&n->entries[tcount]);

		if (failsafe_size > FAILSAFE_SIZE_MAX) {
			/* Abort if the total size of the data in the tags extraordinarily large, */
			exif_mem_free (ne->mem, n->entries[tcount].data);
			exif_log (ne->log, EXIF_LOG_CODE_CORRUPT_DATA,
					  "ExifMnoteCanon", "Failsafe tag size overflow (%lu > %ld)",
					  failsafe_size, FAILSAFE_SIZE_MAX);
			break;
		}

		/* Tag was successfully parsed */
		++tcount;
	}
	/* Store the count of successfully parsed tags */
	n->count = tcount;
}

Add a failsafe on the maximum number of Canon MakerNote subtags.

A malicious file could be crafted to cause extremely large values in some
tags without tripping any buffer range checks.  This is bad with the libexif
representation of Canon MakerNotes because some arrays are turned into
individual tags that the application must loop around.

The largest value I've seen for failsafe_size in a (very small) sample of valid
Canon files is <5000.  The limit is set two orders of magnitude larger to avoid
tripping up falsely in case some models use much larger values.

Patch from Google.

CVE-2020-13114

#define CHECKOVERFLOW(offset,datasize,structsize) (( offset >= datasize) || (structsize > datasize) || (offset > datasize - structsize ))

#define FAILSAFE_SIZE_MAX 1000000L


void makeMaps_d ( DState* s )
{
   Int32 i;
   s->nInUse = 0;
   for (i = 0; i < 256; i++)
      if (s->inUse[i]) {
         s->seqToUnseq[s->nInUse] = i;
         s->nInUse++;
      }
}

Make sure nSelectors is not out of range

nSelectors is used in a loop from 0 to nSelectors to access selectorMtf
which is
	UChar    selectorMtf[BZ_MAX_SELECTORS];
so if nSelectors is bigger than BZ_MAX_SELECTORS it'll do an invalid memory
access

Fixes out of bounds access discovered while fuzzying karchive

#define RETURN(rrr)                               \
   { retVal = rrr; goto save_state_and_return; };

#define GET_BIT(lll,uuu)                          \
   GET_BITS(lll,uuu,1)

#define GET_UCHAR(lll,uuu)                        \
   GET_BITS(lll,uuu,8)

#define GET_BITS(lll,vvv,nnn)                     \
   case lll: s->state = lll;                      \
   while (True) {                                 \
      if (s->bsLive >= nnn) {                     \
         UInt32 v;                                \
         v = (s->bsBuff >>                        \
             (s->bsLive-nnn)) & ((1 << nnn)-1);   \
         s->bsLive -= nnn;                        \
         vvv = v;                                 \
         break;                                   \
      }                                           \
      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
      s->bsBuff                                   \
         = (s->bsBuff << 8) |                     \
           ((UInt32)                              \
              (*((UChar*)(s->strm->next_in))));   \
      s->bsLive += 8;                             \
      s->strm->next_in++;                         \
      s->strm->avail_in--;                        \
      s->strm->total_in_lo32++;                   \
      if (s->strm->total_in_lo32 == 0)            \
         s->strm->total_in_hi32++;                \
   }

#define GET_MTF_VAL(label1,label2,lval)           \
{                                                 \
   if (groupPos == 0) {                           \
      groupNo++;                                  \
      if (groupNo >= nSelectors)                  \
         RETURN(BZ_DATA_ERROR);                   \
      groupPos = BZ_G_SIZE;                       \
      gSel = s->selector[groupNo];                \
      gMinlen = s->minLens[gSel];                 \
      gLimit = &(s->limit[gSel][0]);              \
      gPerm = &(s->perm[gSel][0]);                \
      gBase = &(s->base[gSel][0]);                \
   }                                              \
   groupPos--;                                    \
   zn = gMinlen;                                  \
   GET_BITS(label1, zvec, zn);                    \
   while (1) {                                    \
      if (zn > 20 /* the longest code */)         \
         RETURN(BZ_DATA_ERROR);                   \
      if (zvec <= gLimit[zn]) break;              \
      zn++;                                       \
      GET_BIT(label2, zj);                        \
      zvec = (zvec << 1) | zj;                    \
   };                                             \
   if (zvec - gBase[zn] < 0                       \
       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
      RETURN(BZ_DATA_ERROR);                      \
   lval = gPerm[zvec - gBase[zn]];                \
}


Int32 BZ2_decompress ( DState* s )
{
   UChar      uc;
   Int32      retVal;
   Int32      minLen, maxLen;
   bz_stream* strm = s->strm;

   /* stuff that needs to be saved/restored */
   Int32  i;
   Int32  j;
   Int32  t;
   Int32  alphaSize;
   Int32  nGroups;
   Int32  nSelectors;
   Int32  EOB;
   Int32  groupNo;
   Int32  groupPos;
   Int32  nextSym;
   Int32  nblockMAX;
   Int32  nblock;
   Int32  es;
   Int32  N;
   Int32  curr;
   Int32  zt;
   Int32  zn;
   Int32  zvec;
   Int32  zj;
   Int32  gSel;
   Int32  gMinlen;
   Int32* gLimit;
   Int32* gBase;
   Int32* gPerm;

   if (s->state == BZ_X_MAGIC_1) {
      /*initialise the save area*/
      s->save_i           = 0;
      s->save_j           = 0;
      s->save_t           = 0;
      s->save_alphaSize   = 0;
      s->save_nGroups     = 0;
      s->save_nSelectors  = 0;
      s->save_EOB         = 0;
      s->save_groupNo     = 0;
      s->save_groupPos    = 0;
      s->save_nextSym     = 0;
      s->save_nblockMAX   = 0;
      s->save_nblock      = 0;
      s->save_es          = 0;
      s->save_N           = 0;
      s->save_curr        = 0;
      s->save_zt          = 0;
      s->save_zn          = 0;
      s->save_zvec        = 0;
      s->save_zj          = 0;
      s->save_gSel        = 0;
      s->save_gMinlen     = 0;
      s->save_gLimit      = NULL;
      s->save_gBase       = NULL;
      s->save_gPerm       = NULL;
   }

   /*restore from the save area*/
   i           = s->save_i;
   j           = s->save_j;
   t           = s->save_t;
   alphaSize   = s->save_alphaSize;
   nGroups     = s->save_nGroups;
   nSelectors  = s->save_nSelectors;
   EOB         = s->save_EOB;
   groupNo     = s->save_groupNo;
   groupPos    = s->save_groupPos;
   nextSym     = s->save_nextSym;
   nblockMAX   = s->save_nblockMAX;
   nblock      = s->save_nblock;
   es          = s->save_es;
   N           = s->save_N;
   curr        = s->save_curr;
   zt          = s->save_zt;
   zn          = s->save_zn;
   zvec        = s->save_zvec;
   zj          = s->save_zj;
   gSel        = s->save_gSel;
   gMinlen     = s->save_gMinlen;
   gLimit      = s->save_gLimit;
   gBase       = s->save_gBase;
   gPerm       = s->save_gPerm;

   retVal = BZ_OK;

   switch (s->state) {

      GET_UCHAR(BZ_X_MAGIC_1, uc);
      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_2, uc);
      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_3, uc)
      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
      if (s->blockSize100k < (BZ_HDR_0 + 1) ||
          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
      s->blockSize100k -= BZ_HDR_0;

      if (s->smallDecompress) {
         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
         s->ll4  = BZALLOC(
                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
                   );
         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
      } else {
         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
      }

      GET_UCHAR(BZ_X_BLKHDR_1, uc);

      if (uc == 0x17) goto endhdr_2;
      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_2, uc);
      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_3, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_4, uc);
      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_5, uc);
      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_6, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);

      s->currBlockNo++;
      if (s->verbosity >= 2)
         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );

      s->storedBlockCRC = 0;
      GET_UCHAR(BZ_X_BCRC_1, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_2, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_3, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_4, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);

      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);

      s->origPtr = 0;
      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);

      if (s->origPtr < 0)
         RETURN(BZ_DATA_ERROR);
      if (s->origPtr > 10 + 100000*s->blockSize100k)
         RETURN(BZ_DATA_ERROR);

      /*--- Receive the mapping table ---*/
      for (i = 0; i < 16; i++) {
         GET_BIT(BZ_X_MAPPING_1, uc);
         if (uc == 1)
            s->inUse16[i] = True; else
            s->inUse16[i] = False;
      }

      for (i = 0; i < 256; i++) s->inUse[i] = False;

      for (i = 0; i < 16; i++)
         if (s->inUse16[i])
            for (j = 0; j < 16; j++) {
               GET_BIT(BZ_X_MAPPING_2, uc);
               if (uc == 1) s->inUse[i * 16 + j] = True;
            }
      makeMaps_d ( s );
      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
      alphaSize = s->nInUse+2;

      /*--- Now the selectors ---*/
      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
      if (nSelectors < 1 || nSelectors > BZ_MAX_SELECTORS) RETURN(BZ_DATA_ERROR);
      for (i = 0; i < nSelectors; i++) {
         j = 0;
         while (True) {
            GET_BIT(BZ_X_SELECTOR_3, uc);
            if (uc == 0) break;
            j++;
            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
         }
         s->selectorMtf[i] = j;
      }

      /*--- Undo the MTF values for the selectors. ---*/
      {
         UChar pos[BZ_N_GROUPS], tmp, v;
         for (v = 0; v < nGroups; v++) pos[v] = v;

         for (i = 0; i < nSelectors; i++) {
            v = s->selectorMtf[i];
            tmp = pos[v];
            while (v > 0) { pos[v] = pos[v-1]; v--; }
            pos[0] = tmp;
            s->selector[i] = tmp;
         }
      }

      /*--- Now the coding tables ---*/
      for (t = 0; t < nGroups; t++) {
         GET_BITS(BZ_X_CODING_1, curr, 5);
         for (i = 0; i < alphaSize; i++) {
            while (True) {
               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
               GET_BIT(BZ_X_CODING_2, uc);
               if (uc == 0) break;
               GET_BIT(BZ_X_CODING_3, uc);
               if (uc == 0) curr++; else curr--;
            }
            s->len[t][i] = curr;
         }
      }

      /*--- Create the Huffman decoding tables ---*/
      for (t = 0; t < nGroups; t++) {
         minLen = 32;
         maxLen = 0;
         for (i = 0; i < alphaSize; i++) {
            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
            if (s->len[t][i] < minLen) minLen = s->len[t][i];
         }
         BZ2_hbCreateDecodeTables (
            &(s->limit[t][0]),
            &(s->base[t][0]),
            &(s->perm[t][0]),
            &(s->len[t][0]),
            minLen, maxLen, alphaSize
         );
         s->minLens[t] = minLen;
      }

      /*--- Now the MTF values ---*/

      EOB      = s->nInUse+1;
      nblockMAX = 100000 * s->blockSize100k;
      groupNo  = -1;
      groupPos = 0;

      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;

      /*-- MTF init --*/
      {
         Int32 ii, jj, kk;
         kk = MTFA_SIZE-1;
         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
               kk--;
            }
            s->mtfbase[ii] = kk + 1;
         }
      }
      /*-- end MTF init --*/

      nblock = 0;
      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);

      while (True) {

         if (nextSym == EOB) break;

         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {

            es = -1;
            N = 1;
            do {
               /* Check that N doesn't get too big, so that es doesn't
                  go negative.  The maximum value that can be
                  RUNA/RUNB encoded is equal to the block size (post
                  the initial RLE), viz, 900k, so bounding N at 2
                  million should guard against overflow without
                  rejecting any legitimate inputs. */
               if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR);
               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
               N = N * 2;
               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
            }
               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);

            es++;
            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
            s->unzftab[uc] += es;

            if (s->smallDecompress)
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->ll16[nblock] = (UInt16)uc;
                  nblock++;
                  es--;
               }
            else
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->tt[nblock] = (UInt32)uc;
                  nblock++;
                  es--;
               };

            continue;

         } else {

            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);

            /*-- uc = MTF ( nextSym-1 ) --*/
            {
               Int32 ii, jj, kk, pp, lno, off;
               UInt32 nn;
               nn = (UInt32)(nextSym - 1);

               if (nn < MTFL_SIZE) {
                  /* avoid general-case expense */
                  pp = s->mtfbase[0];
                  uc = s->mtfa[pp+nn];
                  while (nn > 3) {
                     Int32 z = pp+nn;
                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
                     nn -= 4;
                  }
                  while (nn > 0) {
                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
                  };
                  s->mtfa[pp] = uc;
               } else {
                  /* general case */
                  lno = nn / MTFL_SIZE;
                  off = nn % MTFL_SIZE;
                  pp = s->mtfbase[lno] + off;
                  uc = s->mtfa[pp];
                  while (pp > s->mtfbase[lno]) {
                     s->mtfa[pp] = s->mtfa[pp-1]; pp--;
                  };
                  s->mtfbase[lno]++;
                  while (lno > 0) {
                     s->mtfbase[lno]--;
                     s->mtfa[s->mtfbase[lno]]
                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
                     lno--;
                  }
                  s->mtfbase[0]--;
                  s->mtfa[s->mtfbase[0]] = uc;
                  if (s->mtfbase[0] == 0) {
                     kk = MTFA_SIZE-1;
                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
                           kk--;
                        }
                        s->mtfbase[ii] = kk + 1;
                     }
                  }
               }
            }
            /*-- end uc = MTF ( nextSym-1 ) --*/

            s->unzftab[s->seqToUnseq[uc]]++;
            if (s->smallDecompress)
               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
            nblock++;

            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
            continue;
         }
      }

      /* Now we know what nblock is, we can do a better sanity
         check on s->origPtr.
      */
      if (s->origPtr < 0 || s->origPtr >= nblock)
         RETURN(BZ_DATA_ERROR);

      /*-- Set up cftab to facilitate generation of T^(-1) --*/
      /* Check: unzftab entries in range. */
      for (i = 0; i <= 255; i++) {
         if (s->unzftab[i] < 0 || s->unzftab[i] > nblock)
            RETURN(BZ_DATA_ERROR);
      }
      /* Actually generate cftab. */
      s->cftab[0] = 0;
      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
      /* Check: cftab entries in range. */
      for (i = 0; i <= 256; i++) {
         if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
            /* s->cftab[i] can legitimately be == nblock */
            RETURN(BZ_DATA_ERROR);
         }
      }
      /* Check: cftab entries non-descending. */
      for (i = 1; i <= 256; i++) {
         if (s->cftab[i-1] > s->cftab[i]) {
            RETURN(BZ_DATA_ERROR);
         }
      }

      s->state_out_len = 0;
      s->state_out_ch  = 0;
      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
      s->state = BZ_X_OUTPUT;
      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );

      if (s->smallDecompress) {

         /*-- Make a copy of cftab, used in generation of T --*/
         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];

         /*-- compute the T vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->ll16[i]);
            SET_LL(i, s->cftabCopy[uc]);
            s->cftabCopy[uc]++;
         }

         /*-- Compute T^(-1) by pointer reversal on T --*/
         i = s->origPtr;
         j = GET_LL(i);
         do {
            Int32 tmp = GET_LL(j);
            SET_LL(j, i);
            i = j;
            j = tmp;
         }
            while (i != s->origPtr);

         s->tPos = s->origPtr;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_SMALL(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
         } else {
            BZ_GET_SMALL(s->k0); s->nblock_used++;
         }

      } else {

         /*-- compute the T^(-1) vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->tt[i] & 0xff);
            s->tt[s->cftab[uc]] |= (i << 8);
            s->cftab[uc]++;
         }

         s->tPos = s->tt[s->origPtr] >> 8;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_FAST(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
         } else {
            BZ_GET_FAST(s->k0); s->nblock_used++;
         }

      }

      RETURN(BZ_OK);

    endhdr_2:

      GET_UCHAR(BZ_X_ENDHDR_2, uc);
      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_3, uc);
      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_4, uc);
      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_5, uc);
      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_6, uc);
      if (uc != 0x90) RETURN(BZ_DATA_ERROR);

      s->storedCombinedCRC = 0;
      GET_UCHAR(BZ_X_CCRC_1, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_2, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_3, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_4, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);

      s->state = BZ_X_IDLE;
      RETURN(BZ_STREAM_END);

      default: AssertH ( False, 4001 );
   }

   AssertH ( False, 4002 );

   save_state_and_return:

   s->save_i           = i;
   s->save_j           = j;
   s->save_t           = t;
   s->save_alphaSize   = alphaSize;
   s->save_nGroups     = nGroups;
   s->save_nSelectors  = nSelectors;
   s->save_EOB         = EOB;
   s->save_groupNo     = groupNo;
   s->save_groupPos    = groupPos;
   s->save_nextSym     = nextSym;
   s->save_nblockMAX   = nblockMAX;
   s->save_nblock      = nblock;
   s->save_es          = es;
   s->save_N           = N;
   s->save_curr        = curr;
   s->save_zt          = zt;
   s->save_zn          = zn;
   s->save_zvec        = zvec;
   s->save_zj          = zj;
   s->save_gSel        = gSel;
   s->save_gMinlen     = gMinlen;
   s->save_gLimit      = gLimit;
   s->save_gBase       = gBase;
   s->save_gPerm       = gPerm;

   return retVal;
}

#define RETURN(rrr)                               \
   { retVal = rrr; goto save_state_and_return; };

#define GET_BITS(lll,vvv,nnn)                     \
   case lll: s->state = lll;                      \
   while (True) {                                 \
      if (s->bsLive >= nnn) {                     \
         UInt32 v;                                \
         v = (s->bsBuff >>                        \
             (s->bsLive-nnn)) & ((1 << nnn)-1);   \
         s->bsLive -= nnn;                        \
         vvv = v;                                 \
         break;                                   \
      }                                           \
      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
      s->bsBuff                                   \
         = (s->bsBuff << 8) |                     \
           ((UInt32)                              \
              (*((UChar*)(s->strm->next_in))));   \
      s->bsLive += 8;                             \
      s->strm->next_in++;                         \
      s->strm->avail_in--;                        \
      s->strm->total_in_lo32++;                   \
      if (s->strm->total_in_lo32 == 0)            \
         s->strm->total_in_hi32++;                \
   }

#define GET_UCHAR(lll,uuu)                        \
   GET_BITS(lll,uuu,8)

#define GET_BIT(lll,uuu)                          \
   GET_BITS(lll,uuu,1)

#define GET_MTF_VAL(label1,label2,lval)           \
{                                                 \
   if (groupPos == 0) {                           \
      groupNo++;                                  \
      if (groupNo >= nSelectors)                  \
         RETURN(BZ_DATA_ERROR);                   \
      groupPos = BZ_G_SIZE;                       \
      gSel = s->selector[groupNo];                \
      gMinlen = s->minLens[gSel];                 \
      gLimit = &(s->limit[gSel][0]);              \
      gPerm = &(s->perm[gSel][0]);                \
      gBase = &(s->base[gSel][0]);                \
   }                                              \
   groupPos--;                                    \
   zn = gMinlen;                                  \
   GET_BITS(label1, zvec, zn);                    \
   while (1) {                                    \
      if (zn > 20 /* the longest code */)         \
         RETURN(BZ_DATA_ERROR);                   \
      if (zvec <= gLimit[zn]) break;              \
      zn++;                                       \
      GET_BIT(label2, zj);                        \
      zvec = (zvec << 1) | zj;                    \
   };                                             \
   if (zvec - gBase[zn] < 0                       \
       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
      RETURN(BZ_DATA_ERROR);                      \
   lval = gPerm[zvec - gBase[zn]];                \
}