	__acquires(&current->sighand->siglock)
{
	bool gstop_done = false;

	if (arch_ptrace_stop_needed(exit_code, info)) {
		/*
		 * The arch code has something special to do before a
		 * ptrace stop.  This is allowed to block, e.g. for faults
		 * on user stack pages.  We can't keep the siglock while
		 * calling arch_ptrace_stop, so we must release it now.
		 * To preserve proper semantics, we must do this before
		 * any signal bookkeeping like checking group_stop_count.
		 * Meanwhile, a SIGKILL could come in before we retake the
		 * siglock.  That must prevent us from sleeping in TASK_TRACED.
		 * So after regaining the lock, we must check for SIGKILL.
		 */
		spin_unlock_irq(&current->sighand->siglock);
		arch_ptrace_stop(exit_code, info);
		spin_lock_irq(&current->sighand->siglock);
		if (sigkill_pending(current))
			return;
	}

	set_special_state(TASK_TRACED);

	/*
	 * We're committing to trapping.  TRACED should be visible before
	 * TRAPPING is cleared; otherwise, the tracer might fail do_wait().
	 * Also, transition to TRACED and updates to ->jobctl should be
	 * atomic with respect to siglock and should be done after the arch
	 * hook as siglock is released and regrabbed across it.
	 *
	 *     TRACER				    TRACEE
	 *
	 *     ptrace_attach()
	 * [L]   wait_on_bit(JOBCTL_TRAPPING)	[S] set_special_state(TRACED)
	 *     do_wait()
	 *       set_current_state()                smp_wmb();
	 *       ptrace_do_wait()
	 *         wait_task_stopped()
	 *           task_stopped_code()
	 * [L]         task_is_traced()		[S] task_clear_jobctl_trapping();
	 */
	smp_wmb();

	current->last_siginfo = info;
	current->exit_code = exit_code;

	/*
	 * If @why is CLD_STOPPED, we're trapping to participate in a group
	 * stop.  Do the bookkeeping.  Note that if SIGCONT was delievered
	 * across siglock relocks since INTERRUPT was scheduled, PENDING
	 * could be clear now.  We act as if SIGCONT is received after
	 * TASK_TRACED is entered - ignore it.
	 */
	if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING))
		gstop_done = task_participate_group_stop(current);

	/* any trap clears pending STOP trap, STOP trap clears NOTIFY */
	task_clear_jobctl_pending(current, JOBCTL_TRAP_STOP);
	if (info && info->si_code >> 8 == PTRACE_EVENT_STOP)
		task_clear_jobctl_pending(current, JOBCTL_TRAP_NOTIFY);

	/* entering a trap, clear TRAPPING */
	task_clear_jobctl_trapping(current);

	spin_unlock_irq(&current->sighand->siglock);
	read_lock(&tasklist_lock);
	if (may_ptrace_stop()) {
		/*
		 * Notify parents of the stop.
		 *
		 * While ptraced, there are two parents - the ptracer and
		 * the real_parent of the group_leader.  The ptracer should
		 * know about every stop while the real parent is only
		 * interested in the completion of group stop.  The states
		 * for the two don't interact with each other.  Notify
		 * separately unless they're gonna be duplicates.
		 */
		do_notify_parent_cldstop(current, true, why);
		if (gstop_done && ptrace_reparented(current))
			do_notify_parent_cldstop(current, false, why);

		/*
		 * Don't want to allow preemption here, because
		 * sys_ptrace() needs this task to be inactive.
		 *
		 * XXX: implement read_unlock_no_resched().
		 */
		preempt_disable();
		read_unlock(&tasklist_lock);
		cgroup_enter_frozen();
		preempt_enable_no_resched();
		freezable_schedule();
		cgroup_leave_frozen(true);
	} else {
		/*
		 * By the time we got the lock, our tracer went away.
		 * Don't drop the lock yet, another tracer may come.
		 *
		 * If @gstop_done, the ptracer went away between group stop
		 * completion and here.  During detach, it would have set
		 * JOBCTL_STOP_PENDING on us and we'll re-enter
		 * TASK_STOPPED in do_signal_stop() on return, so notifying
		 * the real parent of the group stop completion is enough.
		 */
		if (gstop_done)
			do_notify_parent_cldstop(current, false, why);

		/* tasklist protects us from ptrace_freeze_traced() */
		__set_current_state(TASK_RUNNING);
		if (clear_code)
			current->exit_code = 0;
		read_unlock(&tasklist_lock);
	}

	/*
	 * We are back.  Now reacquire the siglock before touching
	 * last_siginfo, so that we are sure to have synchronized with
	 * any signal-sending on another CPU that wants to examine it.
	 */
	spin_lock_irq(&current->sighand->siglock);
	current->last_siginfo = NULL;

	/* LISTENING can be set only during STOP traps, clear it */
	current->jobctl &= ~JOBCTL_LISTENING;

	/*
	 * Queued signals ignored us while we were stopped for tracing.
	 * So check for any that we should take before resuming user mode.
	 * This sets TIF_SIGPENDING, but never clears it.
	 */
	recalc_sigpending_tsk(current);
}


	__releases(&current->sighand->siglock)
{
	/*
	 * If there are other trap bits pending except JOBCTL_TRAP_FREEZE,
	 * let's make another loop to give it a chance to be handled.
	 * In any case, we'll return back.
	 */
	if ((current->jobctl & (JOBCTL_PENDING_MASK | JOBCTL_TRAP_FREEZE)) !=
	     JOBCTL_TRAP_FREEZE) {
		spin_unlock_irq(&current->sighand->siglock);
		return;
	}

	/*
	 * Now we're sure that there is no pending fatal signal and no
	 * pending traps. Clear TIF_SIGPENDING to not get out of schedule()
	 * immediately (if there is a non-fatal signal pending), and
	 * put the task into sleep.
	 */
	__set_current_state(TASK_INTERRUPTIBLE);
	clear_thread_flag(TIF_SIGPENDING);
	spin_unlock_irq(&current->sighand->siglock);
	cgroup_enter_frozen();
	freezable_schedule();
}


static int check_kill_permission(int sig, struct kernel_siginfo *info,
				 struct task_struct *t)
{
	struct pid *sid;
	int error;

	if (!valid_signal(sig))
		return -EINVAL;

	if (!si_fromuser(info))
		return 0;

	error = audit_signal_info(sig, t); /* Let audit system see the signal */
	if (error)
		return error;

	if (!same_thread_group(current, t) &&
	    !kill_ok_by_cred(t)) {
		switch (sig) {
		case SIGCONT:
			sid = task_session(t);
			/*
			 * We don't return the error if sid == NULL. The
			 * task was unhashed, the caller must notice this.
			 */
			if (!sid || sid == task_session(current))
				break;
			/* fall through */
		default:
			return -EPERM;
		}
	}

	return security_task_kill(t, info, sig, NULL);
}


static inline void input_wakeup_procfs_readers(void) { }


static ssize_t input_dev_show_properties(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	struct input_dev *input_dev = to_input_dev(dev);
	int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit,
				     INPUT_PROP_MAX, true);
	return min_t(int, len, PAGE_SIZE);
}


void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
	switch (type) {
	case EV_KEY:
		__set_bit(code, dev->keybit);
		break;

	case EV_REL:
		__set_bit(code, dev->relbit);
		break;

	case EV_ABS:
		input_alloc_absinfo(dev);
		if (!dev->absinfo)
			return;

		__set_bit(code, dev->absbit);
		break;

	case EV_MSC:
		__set_bit(code, dev->mscbit);
		break;

	case EV_SW:
		__set_bit(code, dev->swbit);
		break;

	case EV_LED:
		__set_bit(code, dev->ledbit);
		break;

	case EV_SND:
		__set_bit(code, dev->sndbit);
		break;

	case EV_FF:
		__set_bit(code, dev->ffbit);
		break;

	case EV_PWR:
		/* do nothing */
		break;

	default:
		pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
		dump_stack();
		return;
	}

	__set_bit(type, dev->evbit);
}


static void devm_input_device_unregister(struct device *dev, void *res)
{
	struct input_devres *devres = res;
	struct input_dev *input = devres->input;

	dev_dbg(dev, "%s: unregistering device %s\n",
		__func__, dev_name(&input->dev));
	__input_unregister_device(input);
}


static int input_dev_freeze(struct device *dev)
{
	struct input_dev *input_dev = to_input_dev(dev);

	spin_lock_irq(&input_dev->event_lock);

	/*
	 * Keys that are pressed now are unlikely to be
	 * still pressed when we resume.
	 */
	input_dev_release_keys(input_dev);

	spin_unlock_irq(&input_dev->event_lock);

	return 0;
}


static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
{
	cit_write_reg(gspca_dev, 0x00FF, 0x0127);
	cit_write_reg(gspca_dev, 0x0004, 0x0124);
	cit_write_reg(gspca_dev, 0x0002, 0x0124);
}


static int cit_start_model4(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	cit_write_reg(gspca_dev, 0x0000, 0x0100);
	cit_write_reg(gspca_dev, 0x00c0, 0x0111);
	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
	cit_write_reg(gspca_dev, 0x0080, 0x012b);
	cit_write_reg(gspca_dev, 0x0000, 0x0108);
	cit_write_reg(gspca_dev, 0x0001, 0x0133);
	cit_write_reg(gspca_dev, 0x009b, 0x010f);
	cit_write_reg(gspca_dev, 0x00bb, 0x010f);
	cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
	cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);

	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
	cit_write_reg(gspca_dev, 0x0004, 0x012f);
	cit_write_reg(gspca_dev, 0xd141, 0x0124);
	cit_write_reg(gspca_dev, 0x0000, 0x0127);
	cit_write_reg(gspca_dev, 0x00fb, 0x012e);
	cit_write_reg(gspca_dev, 0x0000, 0x0130);
	cit_write_reg(gspca_dev, 0x8a28, 0x0124);
	cit_write_reg(gspca_dev, 0x00aa, 0x012f);
	cit_write_reg(gspca_dev, 0xd055, 0x0124);
	cit_write_reg(gspca_dev, 0x000c, 0x0127);
	cit_write_reg(gspca_dev, 0x0009, 0x012e);
	cit_write_reg(gspca_dev, 0xaa28, 0x0124);

	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
	cit_write_reg(gspca_dev, 0x0012, 0x012f);
	cit_write_reg(gspca_dev, 0xd141, 0x0124);
	cit_write_reg(gspca_dev, 0x0008, 0x0127);
	cit_write_reg(gspca_dev, 0x00aa, 0x0130);
	cit_write_reg(gspca_dev, 0x82a8, 0x0124);
	cit_write_reg(gspca_dev, 0x002a, 0x012d);
	cit_write_reg(gspca_dev, 0x0000, 0x012f);
	cit_write_reg(gspca_dev, 0xd145, 0x0124);
	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
	cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);

	switch (gspca_dev->pixfmt.width) {
	case 128: /* 128x96 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);
		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
		cit_write_reg(gspca_dev, 0x0039, 0x010a);
		cit_write_reg(gspca_dev, 0x0001, 0x0102);
		cit_write_reg(gspca_dev, 0x0028, 0x0103);
		cit_write_reg(gspca_dev, 0x0000, 0x0104);
		cit_write_reg(gspca_dev, 0x001e, 0x0105);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0016, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x000a, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0014, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
		cit_write_reg(gspca_dev, 0x001a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
		cit_write_reg(gspca_dev, 0x005a, 0x012d);
		cit_write_reg(gspca_dev, 0x9545, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
		cit_write_reg(gspca_dev, 0x0018, 0x012e);
		cit_write_reg(gspca_dev, 0x0043, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
		cit_write_reg(gspca_dev, 0xd055, 0x0124);
		cit_write_reg(gspca_dev, 0x001c, 0x0127);
		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0032, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0036, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x001e, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0017, 0x0127);
		cit_write_reg(gspca_dev, 0x0013, 0x012e);
		cit_write_reg(gspca_dev, 0x0031, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x0017, 0x012d);
		cit_write_reg(gspca_dev, 0x0078, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
		sd->sof_len = 2;
		break;
	case 160: /* 160x120 */
		cit_write_reg(gspca_dev, 0x0038, 0x0119);
		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
		cit_write_reg(gspca_dev, 0x0001, 0x0102);
		cit_write_reg(gspca_dev, 0x0028, 0x0103);
		cit_write_reg(gspca_dev, 0x0000, 0x0104);
		cit_write_reg(gspca_dev, 0x001e, 0x0105);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0016, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x000b, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0014, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
		cit_write_reg(gspca_dev, 0x001a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
		cit_write_reg(gspca_dev, 0x005a, 0x012d);
		cit_write_reg(gspca_dev, 0x9545, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
		cit_write_reg(gspca_dev, 0x0018, 0x012e);
		cit_write_reg(gspca_dev, 0x0043, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
		cit_write_reg(gspca_dev, 0xd055, 0x0124);
		cit_write_reg(gspca_dev, 0x001c, 0x0127);
		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0032, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0025, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0036, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x001e, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0048, 0x0127);
		cit_write_reg(gspca_dev, 0x0035, 0x012e);
		cit_write_reg(gspca_dev, 0x00d0, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x0048, 0x012d);
		cit_write_reg(gspca_dev, 0x0090, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x0001, 0x0127);
		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
		sd->sof_len = 2;
		break;
	case 176: /* 176x144 */
		cit_write_reg(gspca_dev, 0x0038, 0x0119);
		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
		cit_write_reg(gspca_dev, 0x0001, 0x0102);
		cit_write_reg(gspca_dev, 0x002c, 0x0103);
		cit_write_reg(gspca_dev, 0x0000, 0x0104);
		cit_write_reg(gspca_dev, 0x0024, 0x0105);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0016, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0007, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0014, 0x012d);
		cit_write_reg(gspca_dev, 0x0001, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
		cit_write_reg(gspca_dev, 0x001a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
		cit_write_reg(gspca_dev, 0x005e, 0x012d);
		cit_write_reg(gspca_dev, 0x9545, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
		cit_write_reg(gspca_dev, 0x0018, 0x012e);
		cit_write_reg(gspca_dev, 0x0049, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
		cit_write_reg(gspca_dev, 0xd055, 0x0124);
		cit_write_reg(gspca_dev, 0x001c, 0x0127);
		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0032, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0028, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0036, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x001e, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0010, 0x0127);
		cit_write_reg(gspca_dev, 0x0013, 0x012e);
		cit_write_reg(gspca_dev, 0x002a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x0010, 0x012d);
		cit_write_reg(gspca_dev, 0x006d, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x0001, 0x0127);
		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
		/* TESTME HDG: this does not seem right
		   (it is 2 for all other resolutions) */
		sd->sof_len = 10;
		break;
	case 320: /* 320x240 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);
		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
		cit_write_reg(gspca_dev, 0x0039, 0x010a);
		cit_write_reg(gspca_dev, 0x0001, 0x0102);
		cit_write_reg(gspca_dev, 0x0028, 0x0103);
		cit_write_reg(gspca_dev, 0x0000, 0x0104);
		cit_write_reg(gspca_dev, 0x001e, 0x0105);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0016, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x000a, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0014, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
		cit_write_reg(gspca_dev, 0x001a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
		cit_write_reg(gspca_dev, 0x005a, 0x012d);
		cit_write_reg(gspca_dev, 0x9545, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
		cit_write_reg(gspca_dev, 0x0018, 0x012e);
		cit_write_reg(gspca_dev, 0x0043, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
		cit_write_reg(gspca_dev, 0xd055, 0x0124);
		cit_write_reg(gspca_dev, 0x001c, 0x0127);
		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0032, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0036, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x001e, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0017, 0x0127);
		cit_write_reg(gspca_dev, 0x0013, 0x012e);
		cit_write_reg(gspca_dev, 0x0031, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x0017, 0x012d);
		cit_write_reg(gspca_dev, 0x0078, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
		sd->sof_len = 2;
		break;
	case 352: /* 352x288 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);
		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
		cit_write_reg(gspca_dev, 0x0039, 0x010a);
		cit_write_reg(gspca_dev, 0x0001, 0x0102);
		cit_write_reg(gspca_dev, 0x002c, 0x0103);
		cit_write_reg(gspca_dev, 0x0000, 0x0104);
		cit_write_reg(gspca_dev, 0x0024, 0x0105);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0016, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0006, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0014, 0x012d);
		cit_write_reg(gspca_dev, 0x0002, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
		cit_write_reg(gspca_dev, 0x001a, 0x0130);
		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
		cit_write_reg(gspca_dev, 0x005e, 0x012d);
		cit_write_reg(gspca_dev, 0x9545, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
		cit_write_reg(gspca_dev, 0x0018, 0x012e);
		cit_write_reg(gspca_dev, 0x0049, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
		cit_write_reg(gspca_dev, 0xd055, 0x0124);
		cit_write_reg(gspca_dev, 0x001c, 0x0127);
		cit_write_reg(gspca_dev, 0x00cf, 0x012e);
		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x0032, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
		cit_write_reg(gspca_dev, 0x0036, 0x012d);
		cit_write_reg(gspca_dev, 0x0008, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
		cit_write_reg(gspca_dev, 0x001e, 0x012f);
		cit_write_reg(gspca_dev, 0xd141, 0x0124);
		cit_write_reg(gspca_dev, 0x0010, 0x0127);
		cit_write_reg(gspca_dev, 0x0013, 0x012e);
		cit_write_reg(gspca_dev, 0x0025, 0x0130);
		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
		cit_write_reg(gspca_dev, 0x0010, 0x012d);
		cit_write_reg(gspca_dev, 0x0048, 0x012f);
		cit_write_reg(gspca_dev, 0xd145, 0x0124);
		cit_write_reg(gspca_dev, 0x0000, 0x0127);
		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
		sd->sof_len = 2;
		break;
	}

	cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);

	return 0;
}


static int cit_start_model2(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int clock_div = 0;

	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */
	cit_read_reg(gspca_dev, 0x0116, 0);
	cit_write_reg(gspca_dev, 0x0060, 0x0116);
	cit_write_reg(gspca_dev, 0x0002, 0x0112);
	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
	cit_write_reg(gspca_dev, 0x0008, 0x012b);
	cit_write_reg(gspca_dev, 0x0000, 0x0108);
	cit_write_reg(gspca_dev, 0x0001, 0x0133);
	cit_write_reg(gspca_dev, 0x0001, 0x0102);
	switch (gspca_dev->pixfmt.width) {
	case 176: /* 176x144 */
		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
		cit_write_reg(gspca_dev, 0x00b9, 0x010a);	/* Unique to this mode */
		cit_write_reg(gspca_dev, 0x0038, 0x0119);	/* Unique to this mode */
		/* TESTME HDG: this does not seem right
		   (it is 2 for all other resolutions) */
		sd->sof_len = 10;
		break;
	case 320: /* 320x240 */
		cit_write_reg(gspca_dev, 0x0028, 0x0103);	/* Unique to this mode */
		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
		sd->sof_len = 2;
		break;
#if 0
	case VIDEOSIZE_352x240:
		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
		sd->sof_len = 2;
		break;
#endif
	case 352: /* 352x288 */
		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
		sd->sof_len = 2;
		break;
	}

	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */

	switch (gspca_dev->pixfmt.width) {
	case 176: /* 176x144 */
		cit_write_reg(gspca_dev, 0x0050, 0x0111);
		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
		break;
	case 320: /* 320x240 */
	case 352: /* 352x288 */
		cit_write_reg(gspca_dev, 0x0040, 0x0111);
		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
		break;
	}
	cit_write_reg(gspca_dev, 0x009b, 0x010f);
	cit_write_reg(gspca_dev, 0x00bb, 0x010f);

	/*
	 * Hardware settings, may affect CMOS sensor; not user controls!
	 * -------------------------------------------------------------
	 * 0x0004: no effect
	 * 0x0006: hardware effect
	 * 0x0008: no effect
	 * 0x000a: stops video stream, probably important h/w setting
	 * 0x000c: changes color in hardware manner (not user setting)
	 * 0x0012: changes number of colors (does not affect speed)
	 * 0x002a: no effect
	 * 0x002c: hardware setting (related to scan lines)
	 * 0x002e: stops video stream, probably important h/w setting
	 */
	cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
	cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
	cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
	cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
	cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
	cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
	cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
	cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
	cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);

	/*
	 * Function 0x0030 pops up all over the place. Apparently
	 * it is a hardware control register, with every bit assigned to
	 * do something.
	 */
	cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);

	/*
	 * Magic control of CMOS sensor. Only lower values like
	 * 0-3 work, and picture shifts left or right. Don't change.
	 */
	switch (gspca_dev->pixfmt.width) {
	case 176: /* 176x144 */
		cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
		clock_div = 6;
		break;
	case 320: /* 320x240 */
		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
		cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
		clock_div = 8;
		break;
#if 0
	case VIDEOSIZE_352x240:
		/* This mode doesn't work as Windows programs it; changed to work */
		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
		cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
		clock_div = 10;
		break;
#endif
	case 352: /* 352x288 */
		cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
		clock_div = 16;
		break;
	}

	/* TESTME These are handled through controls
	   KEEP until someone can test leaving this out is ok */
	if (0)
		cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);

	/*
	 * We have our own frame rate setting varying from 0 (slowest) to 6
	 * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
	 # where 0 is also the slowest setting. However for all practical
	 # reasons high settings make no sense because USB is not fast enough
	 # to support high FPS. Be aware that the picture datastream will be
	 # severely disrupted if you ask for frame rate faster than allowed
	 # for the video size - see below:
	 *
	 * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
	 * -----------------------------------------------------------------
	 * 176x144: [6..31]
	 * 320x240: [8..31]
	 * 352x240: [10..31]
	 * 352x288: [16..31] I have to raise lower threshold for stability...
	 *
	 * As usual, slower FPS provides better sensitivity.
	 */
	cit_model2_Packet1(gspca_dev, 0x001c, clock_div);

	/*
	 * This setting does not visibly affect pictures; left it here
	 * because it was present in Windows USB data stream. This function
	 * does not allow arbitrary values and apparently is a bit mask, to
	 * be activated only at appropriate time. Don't change it randomly!
	 */
	switch (gspca_dev->pixfmt.width) {
	case 176: /* 176x144 */
		cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
		break;
	case 320: /* 320x240 */
		cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
		break;
#if 0
	case VIDEOSIZE_352x240:
		cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
		break;
#endif
	case 352: /* 352x288 */
		cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
		break;
	}

	cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
	/* model2 cannot change the backlight compensation while streaming */
	v4l2_ctrl_grab(sd->lighting, true);

	/* color balance rg2 */
	cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
	/* saturation */
	cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
	/* color balance yb */
	cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);

	/* Hardware control command */
	cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);

	return 0;
}


static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	switch (sd->model) {
	case CIT_MODEL0:
	case CIT_MODEL2:
	case CIT_MODEL4:
	case CIT_IBM_NETCAM_PRO:
		/* These models do not have this control */
		break;
	case CIT_MODEL1: {
		int i;
		static const unsigned short sa[] = {
			0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };

		for (i = 0; i < cit_model1_ntries; i++)
			cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
		break;
	}
	case CIT_MODEL3:
	{	/*
		 * "Use a table of magic numbers.
		 *  This setting doesn't really change much.
		 *  But that's how Windows does it."
		 */
		static const struct {
			unsigned short sv1;
			unsigned short sv2;
			unsigned short sv3;
			unsigned short sv4;
		} sv[7] = {
			{ 0x00, 0x00, 0x05, 0x14 },	/* Smoothest */
			{ 0x01, 0x04, 0x05, 0x14 },
			{ 0x02, 0x04, 0x05, 0x14 },
			{ 0x03, 0x04, 0x05, 0x14 },
			{ 0x03, 0x05, 0x05, 0x14 },
			{ 0x03, 0x06, 0x05, 0x14 },
			{ 0x03, 0x07, 0x05, 0x14 }	/* Sharpest */
		};
		cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
		cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
		cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
		cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
		break;
	}
	}
	return 0;
}


static int lookup_node(struct mm_struct *mm, unsigned long addr)
{
	struct page *p;
	int err;

	int locked = 1;
	err = get_user_pages_locked(addr & PAGE_MASK, 1, 0, &p, &locked);
	if (err >= 0) {
		err = page_to_nid(p);
		put_page(p);
	}
	if (locked)
		up_read(&mm->mmap_sem);
	return err;
}


static int migrate_page_add(struct page *page, struct list_head *pagelist,
				unsigned long flags)
{
	return -EIO;
}


static int migrate_page_add(struct page *page, struct list_head *pagelist,
				unsigned long flags)
{
	struct page *head = compound_head(page);
	/*
	 * Avoid migrating a page that is shared with others.
	 */
	if ((flags & MPOL_MF_MOVE_ALL) || page_mapcount(head) == 1) {
		if (!isolate_lru_page(head)) {
			list_add_tail(&head->lru, pagelist);
			mod_node_page_state(page_pgdat(head),
				NR_ISOLATED_ANON + page_is_file_cache(head),
				hpage_nr_pages(head));
		} else if (flags & MPOL_MF_STRICT) {
			/*
			 * Non-movable page may reach here.  And, there may be
			 * temporary off LRU pages or non-LRU movable pages.
			 * Treat them as unmovable pages since they can't be
			 * isolated, so they can't be moved at the moment.  It
			 * should return -EIO for this case too.
			 */
			return -EIO;
		}
	}

	return 0;
}


int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long addr)
{
	struct mempolicy *pol;
	struct zoneref *z;
	int curnid = page_to_nid(page);
	unsigned long pgoff;
	int thiscpu = raw_smp_processor_id();
	int thisnid = cpu_to_node(thiscpu);
	int polnid = NUMA_NO_NODE;
	int ret = -1;

	pol = get_vma_policy(vma, addr);
	if (!(pol->flags & MPOL_F_MOF))
		goto out;

	switch (pol->mode) {
	case MPOL_INTERLEAVE:
		pgoff = vma->vm_pgoff;
		pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
		polnid = offset_il_node(pol, pgoff);
		break;

	case MPOL_PREFERRED:
		if (pol->flags & MPOL_F_LOCAL)
			polnid = numa_node_id();
		else
			polnid = pol->v.preferred_node;
		break;

	case MPOL_BIND:

		/*
		 * allows binding to multiple nodes.
		 * use current page if in policy nodemask,
		 * else select nearest allowed node, if any.
		 * If no allowed nodes, use current [!misplaced].
		 */
		if (node_isset(curnid, pol->v.nodes))
			goto out;
		z = first_zones_zonelist(
				node_zonelist(numa_node_id(), GFP_HIGHUSER),
				gfp_zone(GFP_HIGHUSER),
				&pol->v.nodes);
		polnid = zone_to_nid(z->zone);
		break;

	default:
		BUG();
	}

	/* Migrate the page towards the node whose CPU is referencing it */
	if (pol->flags & MPOL_F_MORON) {
		polnid = thisnid;

		if (!should_numa_migrate_memory(current, page, curnid, thiscpu))
			goto out;
	}

	if (curnid != polnid)
		ret = polnid;
out:
	mpol_cond_put(pol);

	return ret;
}


extract_archive_thread (GSimpleAsyncResult *result,
			GObject            *object,
			GCancellable       *cancellable)
{
	ExtractData          *extract_data;
	LoadData             *load_data;
	GHashTable           *checked_folders;
	GHashTable           *created_files;
	GHashTable           *folders_created_during_extraction;
	GHashTable           *external_links;
	struct archive       *a;
	struct archive_entry *entry;
	int                   r;

	extract_data = g_simple_async_result_get_op_res_gpointer (result);
	load_data = LOAD_DATA (extract_data);

	r = create_read_object (load_data, &a);
	if (r != ARCHIVE_OK) {
		archive_read_free(a);
		return;
	}

	checked_folders = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
	created_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
	folders_created_during_extraction = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
	external_links = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
	fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract);

	while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
		const char    *pathname;
		char          *fullpath;
		const char    *relative_path;
		GFile         *file;
		GFile         *parent;
		GOutputStream *ostream;
		const void    *buffer;
		size_t         buffer_size;
		int64_t        target_offset, actual_offset;
		GError        *local_error = NULL;
		__LA_MODE_T    filetype;

		if (g_cancellable_is_cancelled (cancellable))
			break;

		pathname = archive_entry_pathname (entry);
		if (! extract_data_get_extraction_requested (extract_data, pathname)) {
			archive_read_data_skip (a);
			continue;
		}

		fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
		relative_path = _g_path_get_relative_basename_safe (fullpath, extract_data->base_dir, extract_data->junk_paths);
		if (relative_path == NULL) {
			fr_archive_progress_inc_completed_files (load_data->archive, 1);
			fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
			archive_read_data_skip (a);
			continue;
		}

		if (_g_path_is_external_to_destination (relative_path, extract_data->destination, external_links)) {
			fr_archive_progress_inc_completed_files (load_data->archive, 1);
			fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
			archive_read_data_skip (a);
			continue;
		}

		file = g_file_get_child (extract_data->destination, relative_path);

		/* honor the skip_older and overwrite options */

		if ((g_hash_table_lookup (folders_created_during_extraction, file) == NULL)
		    && (extract_data->skip_older || ! extract_data->overwrite))
		{
			GFileInfo *info;

			info = g_file_query_info (file,
						  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED,
						  G_FILE_QUERY_INFO_NONE,
						  cancellable,
						  &local_error);
			if (info != NULL) {
				gboolean skip = FALSE;

				if (! extract_data->overwrite) {
					skip = TRUE;
				}
				else if (extract_data->skip_older) {
					GTimeVal modification_time;

					g_file_info_get_modification_time (info, &modification_time);
					if (archive_entry_mtime (entry) < modification_time.tv_sec)
						skip = TRUE;
				}

				g_object_unref (info);

				if (skip) {
					g_object_unref (file);

					archive_read_data_skip (a);
					fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);

					if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
						r = ARCHIVE_EOF;
						break;
					}

					continue;
				}
			}
			else {
				if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
					load_data->error = local_error;
					g_object_unref (info);
					break;
				}
				g_clear_error (&local_error);
			}
		}

		fr_archive_progress_inc_completed_files (load_data->archive, 1);

		/* create the file parents */

		parent = g_file_get_parent (file);

		if ((parent != NULL)
		    && (g_hash_table_lookup (checked_folders, parent) == NULL)
		    && ! g_file_query_exists (parent, cancellable))
		{
			if (! _g_file_make_directory_with_parents (parent,
								   folders_created_during_extraction,
								   cancellable,
								   &local_error))
			{
				if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
					load_data->error = local_error;
				else
					g_clear_error (&local_error);
			}

			if (load_data->error == NULL) {
				GFile *grandparent;

				grandparent = g_object_ref (parent);
				while (grandparent != NULL) {
					if (g_hash_table_lookup (checked_folders, grandparent) == NULL)
						g_hash_table_insert (checked_folders, grandparent, GINT_TO_POINTER (1));
					grandparent = g_file_get_parent (grandparent);
				}
			}
		}
		g_object_unref (parent);

		/* create the file */

		filetype = archive_entry_filetype (entry);

		if (load_data->error == NULL) {
			const char  *linkname;

			linkname = archive_entry_hardlink (entry);
			if (linkname != NULL) {
				char        *link_fullpath;
				const char  *relative_path;
				GFile       *link_file;
				char        *oldname;
				char        *newname;
				int          r;

				link_fullpath = (*linkname == '/') ? g_strdup (linkname) : g_strconcat ("/", linkname, NULL);
				relative_path = _g_path_get_relative_basename_safe (link_fullpath, extract_data->base_dir, extract_data->junk_paths);
				if (relative_path == NULL) {
					g_free (link_fullpath);
					archive_read_data_skip (a);
					continue;
				}

				link_file = g_file_get_child (extract_data->destination, relative_path);
				oldname = g_file_get_path (link_file);
				newname = g_file_get_path (file);

				if ((oldname != NULL) && (newname != NULL))
					r = link (oldname, newname);
				else
					r = -1;

				if (r == 0) {
					__LA_INT64_T filesize;

					if (archive_entry_size_is_set (entry))
						filesize = archive_entry_size (entry);
					else
						filesize = -1;

					if (filesize > 0)
						filetype = AE_IFREG; /* treat as a regular file to save the data */
				}
				else {
					char *uri;
					char *msg;

					uri = g_file_get_uri (file);
					msg = g_strdup_printf ("Could not create the hard link %s", uri);
					load_data->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, msg);

					g_free (msg);
					g_free (uri);
				}

				g_free (newname);
				g_free (oldname);
				g_object_unref (link_file);
				g_free (link_fullpath);
			}
		}

		if (load_data->error == NULL) {
			switch (filetype) {
			case AE_IFDIR:
				if (! g_file_make_directory (file, cancellable, &local_error)) {
					if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
						load_data->error = g_error_copy (local_error);
					g_clear_error (&local_error);
				}
				if (load_data->error == NULL)
					g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data));
				archive_read_data_skip (a);
				break;

			case AE_IFREG:
				ostream = (GOutputStream *) g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &load_data->error);
				if (ostream == NULL)
					break;

				actual_offset = 0;
				while ((r = archive_read_data_block (a, &buffer, &buffer_size, &target_offset)) == ARCHIVE_OK) {
					gsize bytes_written;

					if (target_offset > actual_offset) {
						if (! _g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error))
							break;
						fr_archive_progress_inc_completed_bytes (load_data->archive, target_offset - actual_offset);
						actual_offset = target_offset;
					}

					if (! g_output_stream_write_all (ostream, buffer, buffer_size, &bytes_written, cancellable, &load_data->error))
						break;

					actual_offset += bytes_written;
					fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_written);
				}

				if ((r == ARCHIVE_EOF) && (target_offset > actual_offset))
					_g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error);

				_g_object_unref (ostream);

				if (r != ARCHIVE_EOF)
					load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
				else
					g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data));
				break;

			case AE_IFLNK:
				if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error)) {
					if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
						load_data->error = g_error_copy (local_error);
					g_clear_error (&local_error);
				}
				else if (_symlink_is_external_to_destination (file, archive_entry_symlink (entry), extract_data->destination, external_links))
					g_hash_table_insert (external_links, g_object_ref (file), GINT_TO_POINTER (1));
				archive_read_data_skip (a);
				break;

			default:
				archive_read_data_skip (a);
				break;
			}
		}

		g_object_unref (file);
		g_free (fullpath);

		if (load_data->error != NULL)
			break;

		if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
			r = ARCHIVE_EOF;
			break;
		}
	}

	if (load_data->error == NULL)
		restore_original_file_attributes (created_files, cancellable);

	if ((load_data->error == NULL) && (r != ARCHIVE_EOF))
		load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
	if (load_data->error == NULL)
		g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
	if (load_data->error != NULL)
		g_simple_async_result_set_from_error (result, load_data->error);

	g_hash_table_unref (folders_created_during_extraction);
	g_hash_table_unref (created_files);
	g_hash_table_unref (checked_folders);
	g_hash_table_unref (external_links);
	archive_read_free (a);
	extract_data_free (extract_data);
}


extract_data_free (ExtractData *extract_data)
{
	g_free (extract_data->base_dir);
	_g_object_unref (extract_data->destination);
	_g_string_list_free (extract_data->file_list);
	g_hash_table_unref (extract_data->files_to_extract);
	g_hash_table_unref (extract_data->usernames);
	g_hash_table_unref (extract_data->groupnames);
	g_free (extract_data->null_buffer);
	load_data_free (LOAD_DATA (extract_data));
}


fr_archive_libarchive_class_init (FrArchiveLibarchiveClass *klass)
{
	GObjectClass   *gobject_class;
	FrArchiveClass *archive_class;

	fr_archive_libarchive_parent_class = g_type_class_peek_parent (klass);

	gobject_class = G_OBJECT_CLASS (klass);
	gobject_class->finalize = fr_archive_libarchive_finalize;

	archive_class = FR_ARCHIVE_CLASS (klass);
	archive_class->get_mime_types = fr_archive_libarchive_get_mime_types;
	archive_class->get_capabilities = fr_archive_libarchive_get_capabilities;
	archive_class->get_packages = fr_archive_libarchive_get_packages;
	archive_class->list = fr_archive_libarchive_list;
	archive_class->extract_files = fr_archive_libarchive_extract_files;
	archive_class->add_files = fr_archive_libarchive_add_files;
	archive_class->remove_files = fr_archive_libarchive_remove_files;
	archive_class->rename = fr_archive_libarchive_rename;
	archive_class->paste_clipboard = fr_archive_libarchive_paste_clipboard;
	archive_class->add_dropped_files = fr_archive_libarchive_add_dropped_files;
	archive_class->update_open_files = fr_archive_libarchive_update_open_files;
}


_archive_write_file (struct archive       *b,
		     SaveData             *save_data,
		     AddFile              *add_file,
		     gboolean              follow_link,
		     struct archive_entry *r_entry,
		     GCancellable         *cancellable)
{
	LoadData             *load_data = LOAD_DATA (save_data);
	GFileInfo            *info;
	struct archive_entry *w_entry;
	int                   rb;

	/* write the file header */

	info = g_file_query_info (add_file->file,
				  FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY,
				  (! follow_link ? G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS : 0),
				  cancellable,
				  &load_data->error);
	if (info == NULL)
		return WRITE_ACTION_ABORT;

	w_entry = archive_entry_new ();
	if (! _archive_entry_copy_file_info (w_entry, info, save_data)) {
		archive_entry_free (w_entry);
		g_object_unref (info);
		return WRITE_ACTION_SKIP_ENTRY;
	}

	/* honor the update flag */

	if (save_data->update && (r_entry != NULL) && (archive_entry_mtime (w_entry) < archive_entry_mtime (r_entry))) {
		archive_entry_free (w_entry);
		g_object_unref (info);
		return WRITE_ACTION_WRITE_ENTRY;
	}

	archive_entry_set_pathname (w_entry, add_file->pathname);
	rb = archive_write_header (b, w_entry);

	/* write the file data */

	if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
		GInputStream *istream;

		istream = (GInputStream *) g_file_read (add_file->file, cancellable, &load_data->error);
		if (istream != NULL) {
			gssize bytes_read;

			while ((bytes_read = g_input_stream_read (istream, save_data->buffer, save_data->buffer_size, cancellable, &load_data->error)) > 0) {
				archive_write_data (b, save_data->buffer, bytes_read);
				fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_read);
			}

			g_object_unref (istream);
		}
	}

	rb = archive_write_finish_entry (b);

	if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED))
		load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (b));

	archive_entry_free (w_entry);
	g_object_unref (info);

	return (load_data->error == NULL) ? WRITE_ACTION_SKIP_ENTRY : WRITE_ACTION_ABORT;
}


save_archive_thread (GSimpleAsyncResult *result,
		     GObject            *object,
		     GCancellable       *cancellable)
{
	SaveData             *save_data;
	LoadData             *load_data;
	struct archive       *a, *b;
	struct archive_entry *r_entry;
	int                   ra = ARCHIVE_OK, rb = ARCHIVE_OK;

	save_data = g_simple_async_result_get_op_res_gpointer (result);
	load_data = LOAD_DATA (save_data);

	save_data->b = b = archive_write_new ();
	_archive_write_set_format_from_context (b, save_data);
	archive_write_open (b, save_data, save_data_open, save_data_write, save_data_close);
	archive_write_set_bytes_in_last_block (b, 1);

	create_read_object (load_data, &a);

	if (save_data->begin_operation != NULL)
		save_data->begin_operation (save_data, save_data->user_data);

	while ((load_data->error == NULL) && (ra = archive_read_next_header (a, &r_entry)) == ARCHIVE_OK) {
		struct archive_entry *w_entry;
		WriteAction           action;

		if (g_cancellable_is_cancelled (cancellable))
			break;

		action = WRITE_ACTION_WRITE_ENTRY;
		w_entry = archive_entry_clone (r_entry);
		if (save_data->entry_action != NULL)
			action = save_data->entry_action (save_data, w_entry, save_data->user_data);

		if (action == WRITE_ACTION_WRITE_ENTRY) {
			const void   *buffer;
			size_t        buffer_size;
			__LA_INT64_T  offset;

			rb = archive_write_header (b, w_entry);
			if (rb <= ARCHIVE_FAILED) {
				load_data->error = _g_error_new_from_archive_error (archive_error_string (b));
				break;
			}

			switch (archive_entry_filetype (r_entry)) {
			case AE_IFREG:
				while ((ra = archive_read_data_block (a, &buffer, &buffer_size, &offset)) == ARCHIVE_OK) {
					archive_write_data (b, buffer, buffer_size);
					fr_archive_progress_inc_completed_bytes (load_data->archive, buffer_size);
				}

				if (ra <= ARCHIVE_FAILED) {
					load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
					break;
				}
				break;

			default:
				break;
			}

			rb = archive_write_finish_entry (b);
		}
		else if (action == WRITE_ACTION_SKIP_ENTRY)
			fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size (r_entry));

		archive_entry_free (w_entry);
	}

	if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
		ra = ARCHIVE_EOF;

	if (save_data->end_operation != NULL)
		save_data->end_operation (save_data, save_data->user_data);

	rb = archive_write_close (b);

	if ((load_data->error == NULL) && (ra != ARCHIVE_EOF))
		load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
	if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED))
		load_data->error =  _g_error_new_from_archive_error (archive_error_string (b));
	if (load_data->error == NULL)
		g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
	if (load_data->error != NULL)
		g_simple_async_result_set_from_error (result, load_data->error);

	archive_read_free (a);
	archive_write_free (b);
	save_data_free (save_data);
}


static void __exit slcan_exit(void)
{
	int i;
	struct net_device *dev;
	struct slcan *sl;
	unsigned long timeout = jiffies + HZ;
	int busy = 0;

	if (slcan_devs == NULL)
		return;

	/* First of all: check for active disciplines and hangup them.
	 */
	do {
		if (busy)
			msleep_interruptible(100);

		busy = 0;
		for (i = 0; i < maxdev; i++) {
			dev = slcan_devs[i];
			if (!dev)
				continue;
			sl = netdev_priv(dev);
			spin_lock_bh(&sl->lock);
			if (sl->tty) {
				busy++;
				tty_hangup(sl->tty);
			}
			spin_unlock_bh(&sl->lock);
		}
	} while (busy && time_before(jiffies, timeout));

	/* FIXME: hangup is async so we should wait when doing this second
	   phase */

	for (i = 0; i < maxdev; i++) {
		dev = slcan_devs[i];
		if (!dev)
			continue;
		slcan_devs[i] = NULL;

		sl = netdev_priv(dev);
		if (sl->tty) {
			printk(KERN_ERR "%s: tty discipline still running\n",
			       dev->name);
		}

		unregister_netdev(dev);
	}

	kfree(slcan_devs);
	slcan_devs = NULL;

	i = tty_unregister_ldisc(N_SLCAN);
	if (i)
		printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i);
}


static void slc_sync(void)
{
	int i;
	struct net_device *dev;
	struct slcan	  *sl;

	for (i = 0; i < maxdev; i++) {
		dev = slcan_devs[i];
		if (dev == NULL)
			break;

		sl = netdev_priv(dev);
		if (sl->tty)
			continue;
		if (dev->flags & IFF_UP)
			dev_close(dev);
	}
}


static int slcan_open(struct tty_struct *tty)
{
	struct slcan *sl;
	int err;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	if (tty->ops->write == NULL)
		return -EOPNOTSUPP;

	/* RTnetlink lock is misused here to serialize concurrent
	   opens of slcan channels. There are better ways, but it is
	   the simplest one.
	 */
	rtnl_lock();

	/* Collect hanged up channels. */
	slc_sync();

	sl = tty->disc_data;

	err = -EEXIST;
	/* First make sure we're not already connected. */
	if (sl && sl->magic == SLCAN_MAGIC)
		goto err_exit;

	/* OK.  Find a free SLCAN channel to use. */
	err = -ENFILE;
	sl = slc_alloc();
	if (sl == NULL)
		goto err_exit;

	sl->tty = tty;
	tty->disc_data = sl;

	if (!test_bit(SLF_INUSE, &sl->flags)) {
		/* Perform the low-level SLCAN initialization. */
		sl->rcount   = 0;
		sl->xleft    = 0;

		set_bit(SLF_INUSE, &sl->flags);

		err = register_netdevice(sl->dev);
		if (err)
			goto err_free_chan;
	}

	/* Done.  We have linked the TTY line to a channel. */
	rtnl_unlock();
	tty->receive_room = 65536;	/* We don't flow control */

	/* TTY layer expects 0 on success */
	return 0;

err_free_chan:
	sl->tty = NULL;
	tty->disc_data = NULL;
	clear_bit(SLF_INUSE, &sl->flags);
	slc_free_netdev(sl->dev);
	/* do not call free_netdev before rtnl_unlock */
	rtnl_unlock();
	free_netdev(sl->dev);
	return err;

err_exit:
	rtnl_unlock();

	/* Count references from TTY module */
	return err;
}


static void slc_encaps(struct slcan *sl, struct can_frame *cf)
{
	int actual, i;
	unsigned char *pos;
	unsigned char *endpos;
	canid_t id = cf->can_id;

	pos = sl->xbuff;

	if (cf->can_id & CAN_RTR_FLAG)
		*pos = 'R'; /* becomes 'r' in standard frame format (SFF) */
	else
		*pos = 'T'; /* becomes 't' in standard frame format (SSF) */

	/* determine number of chars for the CAN-identifier */
	if (cf->can_id & CAN_EFF_FLAG) {
		id &= CAN_EFF_MASK;
		endpos = pos + SLC_EFF_ID_LEN;
	} else {
		*pos |= 0x20; /* convert R/T to lower case for SFF */
		id &= CAN_SFF_MASK;
		endpos = pos + SLC_SFF_ID_LEN;
	}

	/* build 3 (SFF) or 8 (EFF) digit CAN identifier */
	pos++;
	while (endpos >= pos) {
		*endpos-- = hex_asc_upper[id & 0xf];
		id >>= 4;
	}

	pos += (cf->can_id & CAN_EFF_FLAG) ? SLC_EFF_ID_LEN : SLC_SFF_ID_LEN;

	*pos++ = cf->can_dlc + '0';

	/* RTR frames may have a dlc > 0 but they never have any data bytes */
	if (!(cf->can_id & CAN_RTR_FLAG)) {
		for (i = 0; i < cf->can_dlc; i++)
			pos = hex_byte_pack_upper(pos, cf->data[i]);
	}

	*pos++ = '\r';

	/* Order of next two lines is *very* important.
	 * When we are sending a little amount of data,
	 * the transfer may be completed inside the ops->write()
	 * routine, because it's running with interrupts enabled.
	 * In this case we *never* got WRITE_WAKEUP event,
	 * if we did not request it before write operation.
	 *       14 Oct 1994  Dmitry Gorodchanin.
	 */
	set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
	actual = sl->tty->ops->write(sl->tty, sl->xbuff, pos - sl->xbuff);
	sl->xleft = (pos - sl->xbuff) - actual;
	sl->xhead = sl->xbuff + actual;
	sl->dev->stats.tx_bytes += cf->can_dlc;
}


static void slcan_transmit(struct work_struct *work)
{
	struct slcan *sl = container_of(work, struct slcan, tx_work);
	int actual;

	spin_lock_bh(&sl->lock);
	/* First make sure we're connected. */
	if (!sl->tty || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) {
		spin_unlock_bh(&sl->lock);
		return;
	}

	if (sl->xleft <= 0)  {
		/* Now serial buffer is almost free & we can start
		 * transmission of another packet */
		sl->dev->stats.tx_packets++;
		clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
		spin_unlock_bh(&sl->lock);
		netif_wake_queue(sl->dev);
		return;
	}

	actual = sl->tty->ops->write(sl->tty, sl->xhead, sl->xleft);
	sl->xleft -= actual;
	sl->xhead += actual;
	spin_unlock_bh(&sl->lock);
}


static void JSON_mark(void *ptr)
{
    JSON_Parser *json = ptr;
    rb_gc_mark_maybe(json->Vsource);
    rb_gc_mark_maybe(json->create_id);
    rb_gc_mark_maybe(json->object_class);
    rb_gc_mark_maybe(json->array_class);
    rb_gc_mark_maybe(json->decimal_class);
    rb_gc_mark_maybe(json->match_string);
}


static VALUE cParser_source(VALUE self)
{
    GET_PARSER;
    return rb_str_dup(json->Vsource);
}


vhost_user_set_postcopy_listen(struct virtio_net **pdev,
			struct VhostUserMsg *msg __rte_unused,
			int main_fd __rte_unused)
{
	struct virtio_net *dev = *pdev;

	if (validate_msg_fds(msg, 0) != 0)
		return RTE_VHOST_MSG_RESULT_ERR;

	if (dev->mem && dev->mem->nregions) {
		VHOST_LOG_CONFIG(ERR,
			"Regions already registered at postcopy-listen\n");
		return RTE_VHOST_MSG_RESULT_ERR;
	}
	dev->postcopy_listening = 1;

	return RTE_VHOST_MSG_RESULT_OK;
}


vhost_check_queue_inflights_split(struct virtio_net *dev,
				  struct vhost_virtqueue *vq)
{
	uint16_t i;
	uint16_t resubmit_num = 0, last_io, num;
	struct vring_used *used = vq->used;
	struct rte_vhost_resubmit_info *resubmit;
	struct rte_vhost_inflight_info_split *inflight_split;

	if (!(dev->protocol_features &
	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)))
		return RTE_VHOST_MSG_RESULT_OK;

	/* The frontend may still not support the inflight feature
	 * although we negotiate the protocol feature.
	 */
	if ((!vq->inflight_split))
		return RTE_VHOST_MSG_RESULT_OK;

	if (!vq->inflight_split->version) {
		vq->inflight_split->version = INFLIGHT_VERSION;
		return RTE_VHOST_MSG_RESULT_OK;
	}

	if (vq->resubmit_inflight)
		return RTE_VHOST_MSG_RESULT_OK;

	inflight_split = vq->inflight_split;
	vq->global_counter = 0;
	last_io = inflight_split->last_inflight_io;

	if (inflight_split->used_idx != used->idx) {
		inflight_split->desc[last_io].inflight = 0;
		rte_smp_mb();
		inflight_split->used_idx = used->idx;
	}

	for (i = 0; i < inflight_split->desc_num; i++) {
		if (inflight_split->desc[i].inflight == 1)
			resubmit_num++;
	}

	vq->last_avail_idx += resubmit_num;

	if (resubmit_num) {
		resubmit  = calloc(1, sizeof(struct rte_vhost_resubmit_info));
		if (!resubmit) {
			VHOST_LOG_CONFIG(ERR,
				"failed to allocate memory for resubmit info.\n");
			return RTE_VHOST_MSG_RESULT_ERR;
		}

		resubmit->resubmit_list = calloc(resubmit_num,
			sizeof(struct rte_vhost_resubmit_desc));
		if (!resubmit->resubmit_list) {
			VHOST_LOG_CONFIG(ERR,
				"failed to allocate memory for inflight desc.\n");
			free(resubmit);
			return RTE_VHOST_MSG_RESULT_ERR;
		}

		num = 0;
		for (i = 0; i < vq->inflight_split->desc_num; i++) {
			if (vq->inflight_split->desc[i].inflight == 1) {
				resubmit->resubmit_list[num].index = i;
				resubmit->resubmit_list[num].counter =
					inflight_split->desc[i].counter;
				num++;
			}
		}
		resubmit->resubmit_num = num;

		if (resubmit->resubmit_num > 1)
			qsort(resubmit->resubmit_list, resubmit->resubmit_num,
			      sizeof(struct rte_vhost_resubmit_desc),
			      resubmit_desc_compare);

		vq->global_counter = resubmit->resubmit_list[0].counter + 1;
		vq->resubmit_inflight = resubmit;
	}

	return RTE_VHOST_MSG_RESULT_OK;
}


dump_guest_pages(struct virtio_net *dev)
{
	uint32_t i;
	struct guest_page *page;

	for (i = 0; i < dev->nr_guest_pages; i++) {
		page = &dev->guest_pages[i];

		VHOST_LOG_CONFIG(INFO,
			"guest physical page region %u\n"
			"\t guest_phys_addr: %" PRIx64 "\n"
			"\t host_phys_addr : %" PRIx64 "\n"
			"\t size           : %" PRIx64 "\n",
			i,
			page->guest_phys_addr,
			page->host_phys_addr,
			page->size);
	}
}


static int process_slave_message_reply(struct virtio_net *dev,
				       const struct VhostUserMsg *msg)
{
	struct VhostUserMsg msg_reply;
	int ret;

	if ((msg->flags & VHOST_USER_NEED_REPLY) == 0)
		return 0;

	ret = read_vhost_message(dev->slave_req_fd, &msg_reply);
	if (ret <= 0) {
		if (ret < 0)
			VHOST_LOG_CONFIG(ERR,
				"vhost read slave message reply failed\n");
		else
			VHOST_LOG_CONFIG(INFO,
				"vhost peer closed\n");
		ret = -1;
		goto out;
	}

	ret = 0;
	if (msg_reply.request.slave != msg->request.slave) {
		VHOST_LOG_CONFIG(ERR,
			"Received unexpected msg type (%u), expected %u\n",
			msg_reply.request.slave, msg->request.slave);
		ret = -1;
		goto out;
	}

	ret = msg_reply.payload.u64 ? -1 : 0;

out:
	rte_spinlock_unlock(&dev->slave_req_lock);
	return ret;
}


vhost_user_check_and_alloc_queue_pair(struct virtio_net *dev,
			struct VhostUserMsg *msg)
{
	uint32_t vring_idx;

	switch (msg->request.master) {
	case VHOST_USER_SET_VRING_KICK:
	case VHOST_USER_SET_VRING_CALL:
	case VHOST_USER_SET_VRING_ERR:
		vring_idx = msg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
		break;
	case VHOST_USER_SET_VRING_NUM:
	case VHOST_USER_SET_VRING_BASE:
	case VHOST_USER_SET_VRING_ENABLE:
		vring_idx = msg->payload.state.index;
		break;
	case VHOST_USER_SET_VRING_ADDR:
		vring_idx = msg->payload.addr.index;
		break;
	default:
		return 0;
	}

	if (vring_idx >= VHOST_MAX_VRING) {
		VHOST_LOG_CONFIG(ERR,
			"invalid vring index: %u\n", vring_idx);
		return -1;
	}

	if (dev->virtqueue[vring_idx])
		return 0;

	return alloc_vring_queue(dev, vring_idx);
}


vhost_crypto_close_sess(struct vhost_crypto *vcrypto, uint64_t session_id)
{
	struct rte_cryptodev_sym_session *session;
	uint64_t sess_id = session_id;
	int ret;

	ret = rte_hash_lookup_data(vcrypto->session_map, &sess_id,
			(void **)&session);

	if (unlikely(ret < 0)) {
		VC_LOG_ERR("Failed to delete session %"PRIu64".", session_id);
		return -VIRTIO_CRYPTO_INVSESS;
	}

	if (rte_cryptodev_sym_session_clear(vcrypto->cid, session) < 0) {
		VC_LOG_DBG("Failed to clear session");
		return -VIRTIO_CRYPTO_ERR;
	}

	if (rte_cryptodev_sym_session_free(session) < 0) {
		VC_LOG_DBG("Failed to free session");
		return -VIRTIO_CRYPTO_ERR;
	}

	if (rte_hash_del_key(vcrypto->session_map, &sess_id) < 0) {
		VC_LOG_DBG("Failed to delete session from hash table.");
		return -VIRTIO_CRYPTO_ERR;
	}

	VC_LOG_INFO("Session %"PRIu64" deleted for vdev %i.", sess_id,
			vcrypto->dev->vid);

	return 0;
}


vhost_crypto_finalize_one_request(struct rte_crypto_op *op,
		struct vhost_virtqueue *old_vq)
{
	struct rte_mbuf *m_src = op->sym->m_src;
	struct rte_mbuf *m_dst = op->sym->m_dst;
	struct vhost_crypto_data_req *vc_req = rte_mbuf_to_priv(m_src);
	uint16_t desc_idx;

	if (unlikely(!vc_req)) {
		VC_LOG_ERR("Failed to retrieve vc_req");
		return NULL;
	}

	if (old_vq && (vc_req->vq != old_vq))
		return vc_req->vq;

	desc_idx = vc_req->desc_idx;

	if (unlikely(op->status != RTE_CRYPTO_OP_STATUS_SUCCESS))
		vc_req->inhdr->status = VIRTIO_CRYPTO_ERR;
	else {
		if (vc_req->zero_copy == 0)
			write_back_data(vc_req);
	}

	vc_req->vq->used->ring[desc_idx].id = desc_idx;
	vc_req->vq->used->ring[desc_idx].len = vc_req->len;

	rte_mempool_put(m_src->pool, (void *)m_src);

	if (m_dst)
		rte_mempool_put(m_dst->pool, (void *)m_dst);

	return vc_req->vq;
}


vhost_crypto_process_one_req(struct vhost_crypto *vcrypto,
		struct vhost_virtqueue *vq, struct rte_crypto_op *op,
		struct vring_desc *head, uint16_t desc_idx)
{
	struct vhost_crypto_data_req *vc_req = rte_mbuf_to_priv(op->sym->m_src);
	struct rte_cryptodev_sym_session *session;
	struct virtio_crypto_op_data_req *req, tmp_req;
	struct virtio_crypto_inhdr *inhdr;
	struct vring_desc *desc = NULL;
	uint64_t session_id;
	uint64_t dlen;
	uint32_t nb_descs = vq->size;
	int err = 0;

	vc_req->desc_idx = desc_idx;
	vc_req->dev = vcrypto->dev;
	vc_req->vq = vq;

	if (likely(head->flags & VRING_DESC_F_INDIRECT)) {
		dlen = head->len;
		nb_descs = dlen / sizeof(struct vring_desc);
		/* drop invalid descriptors */
		if (unlikely(nb_descs > vq->size))
			return -1;
		desc = IOVA_TO_VVA(struct vring_desc *, vc_req, head->addr,
				&dlen, VHOST_ACCESS_RO);
		if (unlikely(!desc || dlen != head->len))
			return -1;
		desc_idx = 0;
		head = desc;
	} else {
		desc = head;
	}

	vc_req->head = head;
	vc_req->zero_copy = vcrypto->option;

	req = get_data_ptr(vc_req, desc, VHOST_ACCESS_RO);
	if (unlikely(req == NULL)) {
		switch (vcrypto->option) {
		case RTE_VHOST_CRYPTO_ZERO_COPY_ENABLE:
			err = VIRTIO_CRYPTO_BADMSG;
			VC_LOG_ERR("Invalid descriptor");
			goto error_exit;
		case RTE_VHOST_CRYPTO_ZERO_COPY_DISABLE:
			req = &tmp_req;
			if (unlikely(copy_data(req, vc_req, &desc, sizeof(*req),
					&nb_descs, vq->size) < 0)) {
				err = VIRTIO_CRYPTO_BADMSG;
				VC_LOG_ERR("Invalid descriptor");
				goto error_exit;
			}
			break;
		default:
			err = VIRTIO_CRYPTO_ERR;
			VC_LOG_ERR("Invalid option");
			goto error_exit;
		}
	} else {
		if (unlikely(move_desc(vc_req->head, &desc,
				sizeof(*req), &nb_descs, vq->size) < 0)) {
			VC_LOG_ERR("Incorrect descriptor");
			goto error_exit;
		}
	}

	switch (req->header.opcode) {
	case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
	case VIRTIO_CRYPTO_CIPHER_DECRYPT:
		session_id = req->header.session_id;

		/* one branch to avoid unnecessary table lookup */
		if (vcrypto->cache_session_id != session_id) {
			err = rte_hash_lookup_data(vcrypto->session_map,
					&session_id, (void **)&session);
			if (unlikely(err < 0)) {
				err = VIRTIO_CRYPTO_ERR;
				VC_LOG_ERR("Failed to find session %"PRIu64,
						session_id);
				goto error_exit;
			}

			vcrypto->cache_session = session;
			vcrypto->cache_session_id = session_id;
		}

		session = vcrypto->cache_session;

		err = rte_crypto_op_attach_sym_session(op, session);
		if (unlikely(err < 0)) {
			err = VIRTIO_CRYPTO_ERR;
			VC_LOG_ERR("Failed to attach session to op");
			goto error_exit;
		}

		switch (req->u.sym_req.op_type) {
		case VIRTIO_CRYPTO_SYM_OP_NONE:
			err = VIRTIO_CRYPTO_NOTSUPP;
			break;
		case VIRTIO_CRYPTO_SYM_OP_CIPHER:
			err = prepare_sym_cipher_op(vcrypto, op, vc_req,
					&req->u.sym_req.u.cipher, desc,
					&nb_descs, vq->size);
			break;
		case VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING:
			err = prepare_sym_chain_op(vcrypto, op, vc_req,
					&req->u.sym_req.u.chain, desc,
					&nb_descs, vq->size);
			break;
		}
		if (unlikely(err != 0)) {
			VC_LOG_ERR("Failed to process sym request");
			goto error_exit;
		}
		break;
	default:
		VC_LOG_ERR("Unsupported symmetric crypto request type %u",
				req->header.opcode);
		goto error_exit;
	}

	return 0;

error_exit:

	inhdr = reach_inhdr(vc_req, desc, &nb_descs, vq->size);
	if (likely(inhdr != NULL))
		inhdr->status = (uint8_t)err;

	return -1;
}


get_data_ptr(struct vhost_crypto_data_req *vc_req, struct vring_desc *cur_desc,
		uint8_t perm)
{
	void *data;
	uint64_t dlen = cur_desc->len;

	data = IOVA_TO_VVA(void *, vc_req, cur_desc->addr, &dlen, perm);
	if (unlikely(!data || dlen != cur_desc->len)) {
		VC_LOG_ERR("Failed to map object");
		return NULL;
	}

	return data;
}


vhost_flush_enqueue_batch_packed(struct virtio_net *dev,
				 struct vhost_virtqueue *vq,
				 uint64_t *lens,
				 uint16_t *ids)
{
	uint16_t i;
	uint16_t flags;

	if (vq->shadow_used_idx) {
		do_data_copy_enqueue(dev, vq);
		vhost_flush_enqueue_shadow_packed(dev, vq);
	}

	flags = PACKED_DESC_ENQUEUE_USED_FLAG(vq->used_wrap_counter);

	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
		vq->desc_packed[vq->last_used_idx + i].id = ids[i];
		vq->desc_packed[vq->last_used_idx + i].len = lens[i];
	}

	rte_smp_wmb();

	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
		vq->desc_packed[vq->last_used_idx + i].flags = flags;

	vhost_log_cache_used_vring(dev, vq, vq->last_used_idx *
				   sizeof(struct vring_packed_desc),
				   sizeof(struct vring_packed_desc) *
				   PACKED_BATCH_SIZE);
	vhost_log_cache_sync(dev, vq);

	vq_inc_last_used_packed(vq, PACKED_BATCH_SIZE);
}


rte_vhost_dequeue_burst(int vid, uint16_t queue_id,
	struct rte_mempool *mbuf_pool, struct rte_mbuf **pkts, uint16_t count)
{
	struct virtio_net *dev;
	struct rte_mbuf *rarp_mbuf = NULL;
	struct vhost_virtqueue *vq;
	int16_t success = 1;

	dev = get_device(vid);
	if (!dev)
		return 0;

	if (unlikely(!(dev->flags & VIRTIO_DEV_BUILTIN_VIRTIO_NET))) {
		VHOST_LOG_DATA(ERR,
			"(%d) %s: built-in vhost net backend is disabled.\n",
			dev->vid, __func__);
		return 0;
	}

	if (unlikely(!is_valid_virt_queue_idx(queue_id, 1, dev->nr_vring))) {
		VHOST_LOG_DATA(ERR,
			"(%d) %s: invalid virtqueue idx %d.\n",
			dev->vid, __func__, queue_id);
		return 0;
	}

	vq = dev->virtqueue[queue_id];

	if (unlikely(rte_spinlock_trylock(&vq->access_lock) == 0))
		return 0;

	if (unlikely(vq->enabled == 0)) {
		count = 0;
		goto out_access_unlock;
	}

	if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
		vhost_user_iotlb_rd_lock(vq);

	if (unlikely(vq->access_ok == 0))
		if (unlikely(vring_translate(dev, vq) < 0)) {
			count = 0;
			goto out;
		}

	/*
	 * Construct a RARP broadcast packet, and inject it to the "pkts"
	 * array, to looks like that guest actually send such packet.
	 *
	 * Check user_send_rarp() for more information.
	 *
	 * broadcast_rarp shares a cacheline in the virtio_net structure
	 * with some fields that are accessed during enqueue and
	 * __atomic_compare_exchange_n causes a write if performed compare
	 * and exchange. This could result in false sharing between enqueue
	 * and dequeue.
	 *
	 * Prevent unnecessary false sharing by reading broadcast_rarp first
	 * and only performing compare and exchange if the read indicates it
	 * is likely to be set.
	 */
	if (unlikely(__atomic_load_n(&dev->broadcast_rarp, __ATOMIC_ACQUIRE) &&
			__atomic_compare_exchange_n(&dev->broadcast_rarp,
			&success, 0, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED))) {

		rarp_mbuf = rte_net_make_rarp_packet(mbuf_pool, &dev->mac);
		if (rarp_mbuf == NULL) {
			VHOST_LOG_DATA(ERR, "Failed to make RARP packet.\n");
			count = 0;
			goto out;
		}
		count -= 1;
	}

	if (vq_is_packed(dev)) {
		if (unlikely(dev->dequeue_zero_copy))
			count = virtio_dev_tx_packed_zmbuf(dev, vq, mbuf_pool,
							   pkts, count);
		else
			count = virtio_dev_tx_packed(dev, vq, mbuf_pool, pkts,
						     count);
	} else
		count = virtio_dev_tx_split(dev, vq, mbuf_pool, pkts, count);

out:
	if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
		vhost_user_iotlb_rd_unlock(vq);

out_access_unlock:
	rte_spinlock_unlock(&vq->access_lock);

	if (unlikely(rarp_mbuf != NULL)) {
		/*
		 * Inject it to the head of "pkts" array, so that switch's mac
		 * learning table will get updated first.
		 */
		memmove(&pkts[1], pkts, count * sizeof(struct rte_mbuf *));
		pkts[0] = rarp_mbuf;
		count += 1;
	}

	return count;
}


vhost_user_set_inflight_fd(struct virtio_net **pdev, VhostUserMsg *msg,
			   int main_fd __rte_unused)
{
	uint64_t mmap_size, mmap_offset;
	uint16_t num_queues, queue_size;
	uint32_t pervq_inflight_size;
	struct virtio_net *dev = *pdev;
	void *addr;
	int fd;

	fd = msg->fds[0];
	if (msg->size != sizeof(msg->payload.inflight) || fd < 0) {
		RTE_LOG(ERR, VHOST_CONFIG,
			"invalid set_inflight_fd message size is %d,fd is %d\n",
			msg->size, fd);
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	mmap_size = msg->payload.inflight.mmap_size;
	mmap_offset = msg->payload.inflight.mmap_offset;
	num_queues = msg->payload.inflight.num_queues;
	queue_size = msg->payload.inflight.queue_size;

	if (vq_is_packed(dev))
		pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
	else
		pervq_inflight_size = get_pervq_shm_size_split(queue_size);

	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd mmap_size: %"PRIu64"\n", mmap_size);
	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd mmap_offset: %"PRIu64"\n", mmap_offset);
	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd num_queues: %u\n", num_queues);
	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd queue_size: %u\n", queue_size);
	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd fd: %d\n", fd);
	RTE_LOG(INFO, VHOST_CONFIG,
		"set_inflight_fd pervq_inflight_size: %d\n",
		pervq_inflight_size);

	if (!dev->inflight_info) {
		dev->inflight_info = calloc(1,
					    sizeof(struct inflight_mem_info));
		if (dev->inflight_info == NULL) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"failed to alloc dev inflight area\n");
			return RTE_VHOST_MSG_RESULT_ERR;
		}
	}

	if (dev->inflight_info->addr)
		munmap(dev->inflight_info->addr, dev->inflight_info->size);

	addr = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
		    fd, mmap_offset);
	if (addr == MAP_FAILED) {
		RTE_LOG(ERR, VHOST_CONFIG, "failed to mmap share memory.\n");
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	if (dev->inflight_info->fd)
		close(dev->inflight_info->fd);

	dev->inflight_info->fd = fd;
	dev->inflight_info->addr = addr;
	dev->inflight_info->size = mmap_size;

	return RTE_VHOST_MSG_RESULT_OK;
}


translate_ring_addresses(struct virtio_net *dev, int vq_index)
{
	struct vhost_virtqueue *vq = dev->virtqueue[vq_index];
	struct vhost_vring_addr *addr = &vq->ring_addrs;
	uint64_t len, expected_len;

	if (vq_is_packed(dev)) {
		len = sizeof(struct vring_packed_desc) * vq->size;
		vq->desc_packed = (struct vring_packed_desc *)(uintptr_t)
			ring_addr_to_vva(dev, vq, addr->desc_user_addr, &len);
		vq->log_guest_addr = 0;
		if (vq->desc_packed == NULL ||
				len != sizeof(struct vring_packed_desc) *
				vq->size) {
			RTE_LOG(DEBUG, VHOST_CONFIG,
				"(%d) failed to map desc_packed ring.\n",
				dev->vid);
			return dev;
		}

		dev = numa_realloc(dev, vq_index);
		vq = dev->virtqueue[vq_index];
		addr = &vq->ring_addrs;

		len = sizeof(struct vring_packed_desc_event);
		vq->driver_event = (struct vring_packed_desc_event *)
					(uintptr_t)ring_addr_to_vva(dev,
					vq, addr->avail_user_addr, &len);
		if (vq->driver_event == NULL ||
				len != sizeof(struct vring_packed_desc_event)) {
			RTE_LOG(DEBUG, VHOST_CONFIG,
				"(%d) failed to find driver area address.\n",
				dev->vid);
			return dev;
		}

		len = sizeof(struct vring_packed_desc_event);
		vq->device_event = (struct vring_packed_desc_event *)
					(uintptr_t)ring_addr_to_vva(dev,
					vq, addr->used_user_addr, &len);
		if (vq->device_event == NULL ||
				len != sizeof(struct vring_packed_desc_event)) {
			RTE_LOG(DEBUG, VHOST_CONFIG,
				"(%d) failed to find device area address.\n",
				dev->vid);
			return dev;
		}

		vq->access_ok = 1;
		return dev;
	}

	/* The addresses are converted from QEMU virtual to Vhost virtual. */
	if (vq->desc && vq->avail && vq->used)
		return dev;

	len = sizeof(struct vring_desc) * vq->size;
	vq->desc = (struct vring_desc *)(uintptr_t)ring_addr_to_vva(dev,
			vq, addr->desc_user_addr, &len);
	if (vq->desc == 0 || len != sizeof(struct vring_desc) * vq->size) {
		RTE_LOG(DEBUG, VHOST_CONFIG,
			"(%d) failed to map desc ring.\n",
			dev->vid);
		return dev;
	}

	dev = numa_realloc(dev, vq_index);
	vq = dev->virtqueue[vq_index];
	addr = &vq->ring_addrs;

	len = sizeof(struct vring_avail) + sizeof(uint16_t) * vq->size;
	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
		len += sizeof(uint16_t);
	expected_len = len;
	vq->avail = (struct vring_avail *)(uintptr_t)ring_addr_to_vva(dev,
			vq, addr->avail_user_addr, &len);
	if (vq->avail == 0 || len != expected_len) {
		RTE_LOG(DEBUG, VHOST_CONFIG,
			"(%d) failed to map avail ring.\n",
			dev->vid);
		return dev;
	}

	len = sizeof(struct vring_used) +
		sizeof(struct vring_used_elem) * vq->size;
	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
		len += sizeof(uint16_t);
	expected_len = len;
	vq->used = (struct vring_used *)(uintptr_t)ring_addr_to_vva(dev,
			vq, addr->used_user_addr, &len);
	if (vq->used == 0 || len != expected_len) {
		RTE_LOG(DEBUG, VHOST_CONFIG,
			"(%d) failed to map used ring.\n",
			dev->vid);
		return dev;
	}

	if (vq->last_used_idx != vq->used->idx) {
		RTE_LOG(WARNING, VHOST_CONFIG,
			"last_used_idx (%u) and vq->used->idx (%u) mismatches; "
			"some packets maybe resent for Tx and dropped for Rx\n",
			vq->last_used_idx, vq->used->idx);
		vq->last_used_idx  = vq->used->idx;
		vq->last_avail_idx = vq->used->idx;
	}

	vq->log_guest_addr =
		translate_log_addr(dev, vq, addr->log_guest_addr);
	if (vq->log_guest_addr == 0) {
		RTE_LOG(DEBUG, VHOST_CONFIG,
			"(%d) failed to map log_guest_addr .\n",
			dev->vid);
		return dev;
	}
	vq->access_ok = 1;

	VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) mapped address desc: %p\n",
			dev->vid, vq->desc);
	VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) mapped address avail: %p\n",
			dev->vid, vq->avail);
	VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) mapped address used: %p\n",
			dev->vid, vq->used);
	VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) log_guest_addr: %" PRIx64 "\n",
			dev->vid, vq->log_guest_addr);

	return dev;
}


vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
			int main_fd)
{
	struct virtio_net *dev = *pdev;
	struct VhostUserMemory *memory = &msg->payload.memory;
	struct rte_vhost_mem_region *reg;
	void *mmap_addr;
	uint64_t mmap_size;
	uint64_t mmap_offset;
	uint64_t alignment;
	uint32_t i;
	int populate;
	int fd;

	if (memory->nregions > VHOST_MEMORY_MAX_NREGIONS) {
		RTE_LOG(ERR, VHOST_CONFIG,
			"too many memory regions (%u)\n", memory->nregions);
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	if (dev->mem && !vhost_memory_changed(memory, dev->mem)) {
		RTE_LOG(INFO, VHOST_CONFIG,
			"(%d) memory regions not changed\n", dev->vid);

		for (i = 0; i < memory->nregions; i++)
			close(msg->fds[i]);

		return RTE_VHOST_MSG_RESULT_OK;
	}

	if (dev->mem) {
		free_mem_region(dev);
		rte_free(dev->mem);
		dev->mem = NULL;
	}

	/* Flush IOTLB cache as previous HVAs are now invalid */
	if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
		for (i = 0; i < dev->nr_vring; i++)
			vhost_user_iotlb_flush_all(dev->virtqueue[i]);

	dev->nr_guest_pages = 0;
	if (!dev->guest_pages) {
		dev->max_guest_pages = 8;
		dev->guest_pages = malloc(dev->max_guest_pages *
						sizeof(struct guest_page));
		if (dev->guest_pages == NULL) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"(%d) failed to allocate memory "
				"for dev->guest_pages\n",
				dev->vid);
			return RTE_VHOST_MSG_RESULT_ERR;
		}
	}

	dev->mem = rte_zmalloc("vhost-mem-table", sizeof(struct rte_vhost_memory) +
		sizeof(struct rte_vhost_mem_region) * memory->nregions, 0);
	if (dev->mem == NULL) {
		RTE_LOG(ERR, VHOST_CONFIG,
			"(%d) failed to allocate memory for dev->mem\n",
			dev->vid);
		return RTE_VHOST_MSG_RESULT_ERR;
	}
	dev->mem->nregions = memory->nregions;

	for (i = 0; i < memory->nregions; i++) {
		fd  = msg->fds[i];
		reg = &dev->mem->regions[i];

		reg->guest_phys_addr = memory->regions[i].guest_phys_addr;
		reg->guest_user_addr = memory->regions[i].userspace_addr;
		reg->size            = memory->regions[i].memory_size;
		reg->fd              = fd;

		mmap_offset = memory->regions[i].mmap_offset;

		/* Check for memory_size + mmap_offset overflow */
		if (mmap_offset >= -reg->size) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"mmap_offset (%#"PRIx64") and memory_size "
				"(%#"PRIx64") overflow\n",
				mmap_offset, reg->size);
			goto err_mmap;
		}

		mmap_size = reg->size + mmap_offset;

		/* mmap() without flag of MAP_ANONYMOUS, should be called
		 * with length argument aligned with hugepagesz at older
		 * longterm version Linux, like 2.6.32 and 3.2.72, or
		 * mmap() will fail with EINVAL.
		 *
		 * to avoid failure, make sure in caller to keep length
		 * aligned.
		 */
		alignment = get_blk_size(fd);
		if (alignment == (uint64_t)-1) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"couldn't get hugepage size through fstat\n");
			goto err_mmap;
		}
		mmap_size = RTE_ALIGN_CEIL(mmap_size, alignment);

		populate = (dev->dequeue_zero_copy) ? MAP_POPULATE : 0;
		mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
				 MAP_SHARED | populate, fd, 0);

		if (mmap_addr == MAP_FAILED) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"mmap region %u failed.\n", i);
			goto err_mmap;
		}

		reg->mmap_addr = mmap_addr;
		reg->mmap_size = mmap_size;
		reg->host_user_addr = (uint64_t)(uintptr_t)mmap_addr +
				      mmap_offset;

		if (dev->dequeue_zero_copy)
			if (add_guest_pages(dev, reg, alignment) < 0) {
				RTE_LOG(ERR, VHOST_CONFIG,
					"adding guest pages to region %u failed.\n",
					i);
				goto err_mmap;
			}

		RTE_LOG(INFO, VHOST_CONFIG,
			"guest memory region %u, size: 0x%" PRIx64 "\n"
			"\t guest physical addr: 0x%" PRIx64 "\n"
			"\t guest virtual  addr: 0x%" PRIx64 "\n"
			"\t host  virtual  addr: 0x%" PRIx64 "\n"
			"\t mmap addr : 0x%" PRIx64 "\n"
			"\t mmap size : 0x%" PRIx64 "\n"
			"\t mmap align: 0x%" PRIx64 "\n"
			"\t mmap off  : 0x%" PRIx64 "\n",
			i, reg->size,
			reg->guest_phys_addr,
			reg->guest_user_addr,
			reg->host_user_addr,
			(uint64_t)(uintptr_t)mmap_addr,
			mmap_size,
			alignment,
			mmap_offset);

		if (dev->postcopy_listening) {
			/*
			 * We haven't a better way right now than sharing
			 * DPDK's virtual address with Qemu, so that Qemu can
			 * retrieve the region offset when handling userfaults.
			 */
			memory->regions[i].userspace_addr =
				reg->host_user_addr;
		}
	}
	if (dev->postcopy_listening) {
		/* Send the addresses back to qemu */
		msg->fd_num = 0;
		send_vhost_reply(main_fd, msg);

		/* Wait for qemu to acknolwedge it's got the addresses
		 * we've got to wait before we're allowed to generate faults.
		 */
		VhostUserMsg ack_msg;
		if (read_vhost_message(main_fd, &ack_msg) <= 0) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"Failed to read qemu ack on postcopy set-mem-table\n");
			goto err_mmap;
		}
		if (ack_msg.request.master != VHOST_USER_SET_MEM_TABLE) {
			RTE_LOG(ERR, VHOST_CONFIG,
				"Bad qemu ack on postcopy set-mem-table (%d)\n",
				ack_msg.request.master);
			goto err_mmap;
		}

		/* Now userfault register and we can use the memory */
		for (i = 0; i < memory->nregions; i++) {
#ifdef RTE_LIBRTE_VHOST_POSTCOPY
			reg = &dev->mem->regions[i];
			struct uffdio_register reg_struct;

			/*
			 * Let's register all the mmap'ed area to ensure
			 * alignment on page boundary.
			 */
			reg_struct.range.start =
				(uint64_t)(uintptr_t)reg->mmap_addr;
			reg_struct.range.len = reg->mmap_size;
			reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;

			if (ioctl(dev->postcopy_ufd, UFFDIO_REGISTER,
						&reg_struct)) {
				RTE_LOG(ERR, VHOST_CONFIG,
					"Failed to register ufd for region %d: (ufd = %d) %s\n",
					i, dev->postcopy_ufd,
					strerror(errno));
				goto err_mmap;
			}
			RTE_LOG(INFO, VHOST_CONFIG,
				"\t userfaultfd registered for range : %llx - %llx\n",
				reg_struct.range.start,
				reg_struct.range.start +
				reg_struct.range.len - 1);
#else
			goto err_mmap;
#endif
		}
	}

	for (i = 0; i < dev->nr_vring; i++) {
		struct vhost_virtqueue *vq = dev->virtqueue[i];

		if (vq->desc || vq->avail || vq->used) {
			/*
			 * If the memory table got updated, the ring addresses
			 * need to be translated again as virtual addresses have
			 * changed.
			 */
			vring_invalidate(dev, vq);

			dev = translate_ring_addresses(dev, i);
			if (!dev) {
				dev = *pdev;
				goto err_mmap;
			}

			*pdev = dev;
		}
	}

	dump_guest_pages(dev);

	return RTE_VHOST_MSG_RESULT_OK;

err_mmap:
	free_mem_region(dev);
	rte_free(dev->mem);
	dev->mem = NULL;
	return RTE_VHOST_MSG_RESULT_ERR;
}


vhost_user_get_inflight_fd(struct virtio_net **pdev,
			   VhostUserMsg *msg,
			   int main_fd __rte_unused)
{
	struct rte_vhost_inflight_info_packed *inflight_packed;
	uint64_t pervq_inflight_size, mmap_size;
	uint16_t num_queues, queue_size;
	struct virtio_net *dev = *pdev;
	int fd, i, j;
	void *addr;

	if (msg->size != sizeof(msg->payload.inflight)) {
		VHOST_LOG_CONFIG(ERR,
			"invalid get_inflight_fd message size is %d\n",
			msg->size);
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	if (dev->inflight_info == NULL) {
		dev->inflight_info = calloc(1,
					    sizeof(struct inflight_mem_info));
		if (!dev->inflight_info) {
			VHOST_LOG_CONFIG(ERR,
				"failed to alloc dev inflight area\n");
			return RTE_VHOST_MSG_RESULT_ERR;
		}
		dev->inflight_info->fd = -1;
	}

	num_queues = msg->payload.inflight.num_queues;
	queue_size = msg->payload.inflight.queue_size;

	VHOST_LOG_CONFIG(INFO, "get_inflight_fd num_queues: %u\n",
		msg->payload.inflight.num_queues);
	VHOST_LOG_CONFIG(INFO, "get_inflight_fd queue_size: %u\n",
		msg->payload.inflight.queue_size);

	if (vq_is_packed(dev))
		pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
	else
		pervq_inflight_size = get_pervq_shm_size_split(queue_size);

	mmap_size = num_queues * pervq_inflight_size;
	addr = inflight_mem_alloc("vhost-inflight", mmap_size, &fd);
	if (!addr) {
		VHOST_LOG_CONFIG(ERR,
			"failed to alloc vhost inflight area\n");
			msg->payload.inflight.mmap_size = 0;
		return RTE_VHOST_MSG_RESULT_ERR;
	}
	memset(addr, 0, mmap_size);

	if (dev->inflight_info->addr) {
		munmap(dev->inflight_info->addr, dev->inflight_info->size);
		dev->inflight_info->addr = NULL;
	}

	if (dev->inflight_info->fd >= 0) {
		close(dev->inflight_info->fd);
		dev->inflight_info->fd = -1;
	}

	dev->inflight_info->addr = addr;
	dev->inflight_info->size = msg->payload.inflight.mmap_size = mmap_size;
	dev->inflight_info->fd = msg->fds[0] = fd;
	msg->payload.inflight.mmap_offset = 0;
	msg->fd_num = 1;

	if (vq_is_packed(dev)) {
		for (i = 0; i < num_queues; i++) {
			inflight_packed =
				(struct rte_vhost_inflight_info_packed *)addr;
			inflight_packed->used_wrap_counter = 1;
			inflight_packed->old_used_wrap_counter = 1;
			for (j = 0; j < queue_size; j++)
				inflight_packed->desc[j].next = j + 1;
			addr = (void *)((char *)addr + pervq_inflight_size);
		}
	}

	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_size: %"PRIu64"\n",
		msg->payload.inflight.mmap_size);
	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_offset: %"PRIu64"\n",
		msg->payload.inflight.mmap_offset);
	VHOST_LOG_CONFIG(INFO,
		"send inflight fd: %d\n", msg->fds[0]);

	return RTE_VHOST_MSG_RESULT_REPLY;
}


outputCode (Int64 code, Int64 &c, int &lc, char *&out)
{
    outputBits (hufLength (code), hufCode (code), c, lc, out);
}


TileBufferTask::TileBufferTask
    (TaskGroup *group,
     DeepTiledInputFile::Data *ifd,
     TileBuffer *tileBuffer)
:
    Task (group),
    _ifd (ifd),
    _tileBuffer (tileBuffer)
{
    // empty
}


lineBufferMinY (int y, int minY, int linesInLineBuffer)
{
    return ((y - minY) / linesInLineBuffer) * linesInLineBuffer + minY;
}


convertInPlace (char *& writePtr,
                const char *& readPtr,
		PixelType type,
                size_t numPixels)
{
    switch (type)
    {
      case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:

        for (size_t j = 0; j < numPixels; ++j)
        {
            Xdr::write <CharPtrIO> (writePtr, *(const unsigned int *) readPtr);
            readPtr += sizeof(unsigned int);
        }
        break;

      case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:

        for (size_t j = 0; j < numPixels; ++j)
        {
            Xdr::write <CharPtrIO> (writePtr, *(const half *) readPtr);
            readPtr += sizeof(half);
        }
        break;

      case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:

        for (size_t j = 0; j < numPixels; ++j)
        {
            union bytesOrFloat tmp;
            tmp.b = * reinterpret_cast<const FBytes *>( readPtr );
            Xdr::write <CharPtrIO> (writePtr, tmp.f);
            readPtr += sizeof(float);
        }
        break;

      default:

        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
    }
}


void setInputFrameBuffer(FrameBuffer& frameBuffer, int pixelType,
                         Array2D<unsigned int>& uData, Array2D<float>& fData,
                         Array2D<half>& hData, int width, int height)
{
    switch (pixelType)
    {
        case 0:
            uData.resizeErase(height, width);
            frameBuffer.insert ("UINT",
                                Slice (IMF::UINT,
                                (char *) (&uData[0][0]),
                                sizeof (uData[0][0]) * 1,
                                sizeof (uData[0][0]) * width,
                                1, 1,
                                0));
            break;
        case 1:
            fData.resizeErase(height, width);
            frameBuffer.insert ("FLOAT",
                                Slice (IMF::FLOAT,
                                (char *) (&fData[0][0]),
                                sizeof (fData[0][0]) * 1,
                                sizeof (fData[0][0]) * width,
                                1, 1,
                                0));
            break;
        case 2:
            hData.resizeErase(height, width);
            frameBuffer.insert ("HALF",
                                Slice (IMF::HALF,
                                (char *) (&hData[0][0]),
                                sizeof (hData[0][0]) * 1,
                                sizeof (hData[0][0]) * width,
                                1, 1,
                                0));
            break;
    }
}


ScanLineInputFile::readPixels (int scanLine)
{
    readPixels (scanLine, scanLine);
}


PizCompressor::compressTile (const char *inPtr,
			     int inSize,
			     IMATH_NAMESPACE::Box2i range,
			     const char *&outPtr)
{
    return compress (inPtr, inSize, range, outPtr);
}


TiledInputFile::fileName () const
{
    return _data->_streamData->is->fileName();
}


PizCompressor::uncompressTile (const char *inPtr,
			       int inSize,
			       IMATH_NAMESPACE::Box2i range,
			       const char *&outPtr)
{
    return uncompress (inPtr, inSize, range, outPtr);
}


offsetInLineBufferTable (const vector<size_t> &bytesPerLine,
			 int linesInLineBuffer,
			 vector<size_t> &offsetInLineBuffer)
{
    offsetInLineBufferTable (bytesPerLine,
                             0, bytesPerLine.size() - 1,
                             linesInLineBuffer,
                             offsetInLineBuffer);
}


rleCompress (int inLength, const char in[], signed char out[])
{
    const char *inEnd = in + inLength;
    const char *runStart = in;
    const char *runEnd = in + 1;
    signed char *outWrite = out;

    while (runStart < inEnd)
    {
	while (runEnd < inEnd &&
	       *runStart == *runEnd &&
	       runEnd - runStart - 1 < MAX_RUN_LENGTH)
	{
	    ++runEnd;
	}

	if (runEnd - runStart >= MIN_RUN_LENGTH)
	{
	    //
	    // Compressable run
	    //

	    *outWrite++ = (runEnd - runStart) - 1;
	    *outWrite++ = *(signed char *) runStart;
	    runStart = runEnd;
	}
	else
	{
	    //
	    // Uncompressable run
	    //

	    while (runEnd < inEnd &&
		   ((runEnd + 1 >= inEnd ||
		     *runEnd != *(runEnd + 1)) ||
		    (runEnd + 2 >= inEnd ||
		     *(runEnd + 1) != *(runEnd + 2))) &&
		   runEnd - runStart < MAX_RUN_LENGTH)
	    {
		++runEnd;
	    }

	    *outWrite++ = runStart - runEnd;

	    while (runStart < runEnd)
	    {
		*outWrite++ = *(signed char *) (runStart++);
	    }
	}

	++runEnd;
    }

    return outWrite - out;
}


roundToNextMultiple(int n, int d)
{
    return ((n + d - 1) / d) * d;
}


initialize (Header &header,
	    const Box2i &displayWindow,
	    const Box2i &dataWindow,
	    float pixelAspectRatio,
	    const V2f &screenWindowCenter,
	    float screenWindowWidth,
	    LineOrder lineOrder,
	    Compression compression)
{
    header.insert ("displayWindow", Box2iAttribute (displayWindow));
    header.insert ("dataWindow", Box2iAttribute (dataWindow));
    if ( !std::isnormal (pixelAspectRatio) || pixelAspectRatio < 0.f)
        THROW (IEX_NAMESPACE::ArgExc, "Invalid pixel aspect ratio");
    header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
    header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
    header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
    header.insert ("lineOrder", LineOrderAttribute (lineOrder));
    header.insert ("compression", CompressionAttribute (compression));
    header.insert ("channels", ChannelListAttribute ());
}


Header::dataWindow () const
{
    return static_cast <const Box2iAttribute &>
	((*this)["dataWindow"]).value();
}


getBits (int nBits, Int64 &c, int &lc, const char *&in)
{
    while (lc < nBits)
    {
	c = (c << 8) | *(unsigned char *)(in++);
	lc += 8;
    }

    lc -= nBits;
    return (c >> lc) & ((1 << nBits) - 1);
}


TiledInputFile::isComplete () const
{
    return _data->fileIsComplete;
}


Header::setTileDescription(const TileDescription& td)
{
    insert ("tiles", TileDescriptionAttribute (td));
}


void DeepScanLineInputFile::readPixelSampleCounts (const char* rawPixelData,
                                                   const DeepFrameBuffer& frameBuffer,
                                                   int scanLine1,
                                                   int scanLine2) const
{
    //
    // read header from block - already converted from Xdr to native format
    //
    int data_scanline = *(int *) rawPixelData;
    Int64 sampleCountTableDataSize=*(Int64 *) (rawPixelData+4);

    int maxY;
    maxY = min(data_scanline + _data->linesInBuffer - 1, _data->maxY);

    if(scanLine1 != data_scanline)
    {
        THROW(IEX_NAMESPACE::ArgExc,"readPixelSampleCounts(rawPixelData,frameBuffer,"<< scanLine1 << ',' << scanLine2 << ") called with incorrect start scanline - should be " << data_scanline );
    }

    if(scanLine2 != maxY)
    {
        THROW(IEX_NAMESPACE::ArgExc,"readPixelSampleCounts(rawPixelData,frameBuffer,"<< scanLine1 << ',' << scanLine2 << ") called with incorrect end scanline - should be " << maxY );
    }

    //
    // If the sample count table is compressed, we'll uncompress it.
    //

    Int64 rawSampleCountTableSize = (maxY - data_scanline + 1) * (_data->maxX - _data->minX + 1) *
    Xdr::size <unsigned int> ();

    Compressor * decomp=NULL;
    const char* readPtr;
    if (sampleCountTableDataSize < rawSampleCountTableSize)
    {
        decomp = newCompressor(_data->header.compression(),
                               rawSampleCountTableSize,
                               _data->header);

        decomp->uncompress(rawPixelData+28,
                                               sampleCountTableDataSize,
                                               data_scanline,
                                               readPtr);
    }
    else readPtr = rawPixelData+28;

    char* base = frameBuffer.getSampleCountSlice().base;
    int xStride = frameBuffer.getSampleCountSlice().xStride;
    int yStride = frameBuffer.getSampleCountSlice().yStride;

    for (int y = scanLine1; y <= scanLine2; y++)
    {
        int lastAccumulatedCount = 0;
        for (int x = _data->minX; x <= _data->maxX; x++)
        {
            int accumulatedCount, count;

            //
            // Read the sample count for pixel (x, y).
            //

            Xdr::read <CharPtrIO> (readPtr, accumulatedCount);
            if (x == _data->minX)
                count = accumulatedCount;
            else
                count = accumulatedCount - lastAccumulatedCount;
            lastAccumulatedCount = accumulatedCount;

            //
            // Store the data in both internal and external data structure.
            //

            sampleCount(base, xStride, yStride, x, y) = count;
        }
    }

    if(decomp)
    {
       delete decomp;
    }
}


Header::setChunkCount(int chunks)
{
    insert("chunkCount",IntAttribute(chunks));
}


InputFile::InputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
    _data (new Data (numThreads))
{
    _data->_streamData=NULL;
    _data->_deleteStream=false;
    try
    {
        readMagicNumberAndVersionField(is, _data->version);

        //
        // Backward compatibility to read multpart file.
        //
        if (isMultiPart(_data->version))
        {
            compatibilityInitialize(is);
        }
        else
        {
            _data->_streamData = new InputStreamMutex();
            _data->_streamData->is = &is;
            _data->header.readFrom (*_data->_streamData->is, _data->version);

            // fix type attribute in single part regular image types
            // (may be wrong if an old version of OpenEXR converts
            // a tiled image to scanline or vice versa)
            if(!isNonImage(_data->version)  &&
               !isMultiPart(_data->version) &&
               _data->header.hasType())
            {
                _data->header.setType(isTiled(_data->version) ? TILEDIMAGE : SCANLINEIMAGE);
            }

            _data->header.sanityCheck (isTiled (_data->version));

            initialize();
        }
    }
    catch (IEX_NAMESPACE::BaseExc &e)
    {
        if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
        if (_data)       delete _data;
        _data=NULL;

        REPLACE_EXC (e, "Cannot read image file "
                     "\"" << is.fileName() << "\". " << e.what());
        throw;
    }
    catch (...)
    {
        if (_data &&  !_data->multiPartBackwardSupport  && _data->_streamData) delete _data->_streamData;
        if (_data)       delete _data;
        _data=NULL;
        throw;
    }
}


readPixelData (InputStreamMutex *streamData,
               DeepScanLineInputFile::Data *ifd,
               int minY,
               char *&buffer,
               Int64 &packedDataSize,
               Int64 &unpackedDataSize)
{
    //
    // Read a single line buffer from the input file.
    //
    // If the input file is not memory-mapped, we copy the pixel data into
    // into the array pointed to by buffer.  If the file is memory-mapped,
    // then we change where buffer points to instead of writing into the
    // array (hence buffer needs to be a reference to a char *).
    //

    int lineBufferNumber = (minY - ifd->minY) / ifd->linesInBuffer;

    Int64 lineOffset = ifd->lineOffsets[lineBufferNumber];

    if (lineOffset == 0)
        THROW (IEX_NAMESPACE::InputExc, "Scan line " << minY << " is missing.");

    //
    // Seek to the start of the scan line in the file,
    // if necessary.
    //

    if (!isMultiPart(ifd->version))
    {
        if (ifd->nextLineBufferMinY != minY)
            streamData->is->seekg (lineOffset);
    }
    else
    {
        //
        // In a multi-part file, the file pointer may have been moved by
        // other parts, so we have to ask tellg() where we are.
        //
        if (streamData->is->tellg() != ifd->lineOffsets[lineBufferNumber])
            streamData->is->seekg (lineOffset);
    }

    //
    // Read the data block's header.
    //

    int yInFile;

    //
    // Read the part number when we are dealing with a multi-part file.
    //

    if (isMultiPart(ifd->version))
    {
        int partNumber;
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, partNumber);
        if (partNumber != ifd->partNumber)
        {
            THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
                   << ", should be " << ifd->partNumber << ".");
        }
    }

    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, yInFile);

    if (yInFile != minY)
        throw IEX_NAMESPACE::InputExc ("Unexpected data block y coordinate.");

    Int64 sampleCountTableSize;
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, sampleCountTableSize);
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, packedDataSize);
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, unpackedDataSize);

    //
    // We make a check on the data size requirements here.
    // Whilst we wish to store 64bit sizes on disk, not all the compressors
    // have been made to work with such data sizes and are still limited to
    // using signed 32 bit (int) for the data size. As such, this version
    // insists that we validate that the data size does not exceed the data
    // type max limit.
    // @TODO refactor the compressor code to ensure full 64-bit support.
    //

    int compressorMaxDataSize = std::numeric_limits<int>::max();
    if (packedDataSize   > Int64(compressorMaxDataSize) ||
        unpackedDataSize > Int64(compressorMaxDataSize))
    {
        THROW (IEX_NAMESPACE::ArgExc, "This version of the library does not support "
              << "the allocation of data with size  > " << compressorMaxDataSize
              << " file unpacked size :" << unpackedDataSize
              << " file packed size   :" << packedDataSize << ".\n");
    }

    //
    // Skip the pixel sample count table because we have read this data.
    //

    Xdr::skip <StreamIO> (*streamData->is, sampleCountTableSize);

    //
    // Read the pixel data.
    //

    if (streamData->is->isMemoryMapped ())
        buffer = streamData->is->readMemoryMapped (packedDataSize);
    else
    {
        // (TODO) check if the packed data size is too big?
        // (TODO) better memory management. Don't delete buffer all the time.
        if (buffer != 0) delete[] buffer;
        buffer = new char[packedDataSize];
        streamData->is->read (buffer, packedDataSize);
    }

    //
    // Keep track of which scan line is the next one in
    // the file, so that we can avoid redundant seekg()
    // operations (seekg() can be fairly expensive).
    //

    if (ifd->lineOrder == INCREASING_Y)
        ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
    else
        ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
}


Header::operator [] (const string &name)
{
    return this->operator[] (name.c_str());
}