static void compat_flush_offsets(void)
{
	struct compat_delta *tmp, *next;

	if (compat_offsets) {
		for(tmp = compat_offsets; tmp; tmp = next) {
			next = tmp->next;
			kfree(tmp);
		}
		compat_offsets = NULL;
	}
}


struct xt_target *xt_request_find_target(int af, const char *name, u8 revision)
{
	struct xt_target *target;

	target = try_then_request_module(xt_find_target(af, name, revision),
					 "%st_%s", xt_prefix[af], name);
	if (IS_ERR(target) || !target)
		return NULL;
	return target;
}


__do_replace(const char *name, unsigned int valid_hooks,
		struct xt_table_info *newinfo, unsigned int num_counters,
		void __user *counters_ptr)
{
	int ret;
	struct ipt_table *t;
	struct xt_table_info *oldinfo;
	struct xt_counters *counters;
	void *loc_cpu_old_entry;

	ret = 0;
	counters = vmalloc(num_counters * sizeof(struct xt_counters));
	if (!counters) {
		ret = -ENOMEM;
		goto out;
	}

	t = try_then_request_module(xt_find_table_lock(AF_INET, name),
				    "iptable_%s", name);
	if (!t || IS_ERR(t)) {
		ret = t ? PTR_ERR(t) : -ENOENT;
		goto free_newinfo_counters_untrans;
	}

	/* You lied! */
	if (valid_hooks != t->valid_hooks) {
		duprintf("Valid hook crap: %08X vs %08X\n",
			 valid_hooks, t->valid_hooks);
		ret = -EINVAL;
		goto put_module;
	}

	oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
	if (!oldinfo)
		goto put_module;

	/* Update module usage count based on number of rules */
	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
		oldinfo->number, oldinfo->initial_entries, newinfo->number);
	if ((oldinfo->number > oldinfo->initial_entries) ||
	    (newinfo->number <= oldinfo->initial_entries))
		module_put(t->me);
	if ((oldinfo->number > oldinfo->initial_entries) &&
	    (newinfo->number <= oldinfo->initial_entries))
		module_put(t->me);

	/* Get the old counters. */
	get_counters(oldinfo, counters);
	/* Decrease module usage counts and free resource */
	loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
	IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
	xt_free_table_info(oldinfo);
	if (copy_to_user(counters_ptr, counters,
			 sizeof(struct xt_counters) * num_counters) != 0)
		ret = -EFAULT;
	vfree(counters);
	xt_table_unlock(t);
	return ret;

 put_module:
	module_put(t->me);
	xt_table_unlock(t);
 free_newinfo_counters_untrans:
	vfree(counters);
 out:
	return ret;
}


int xt_check_target(const struct xt_target *target, unsigned short family,
		    unsigned int size, const char *table, unsigned int hook_mask,
		    unsigned short proto, int inv_proto)
{
	if (XT_ALIGN(target->targetsize) != size) {
		printk("%s_tables: %s target: invalid size %Zu != %u\n",
		       xt_prefix[family], target->name,
		       XT_ALIGN(target->targetsize), size);
		return -EINVAL;
	}
	if (target->table && strcmp(target->table, table)) {
		printk("%s_tables: %s target: only valid in %s table, not %s\n",
		       xt_prefix[family], target->name, target->table, table);
		return -EINVAL;
	}
	if (target->hooks && (hook_mask & ~target->hooks) != 0) {
		printk("%s_tables: %s target: bad hook_mask %u\n",
		       xt_prefix[family], target->name, hook_mask);
		return -EINVAL;
	}
	if (target->proto && (target->proto != proto || inv_proto)) {
		printk("%s_tables: %s target: only valid for protocol %u\n",
		       xt_prefix[family], target->name, target->proto);
		return -EINVAL;
	}
	return 0;
}


static void compat_standard_from_user(void *dst, void *src)
{
	int v = *(compat_int_t *)src;

	if (v > 0)
		v += compat_calc_jump(v);
	memcpy(dst, &v, sizeof(v));
}


translate_compat_table(const char *name,
		unsigned int valid_hooks,
		struct xt_table_info **pinfo,
		void **pentry0,
		unsigned int total_size,
		unsigned int number,
		unsigned int *hook_entries,
		unsigned int *underflows)
{
	unsigned int i;
	struct xt_table_info *newinfo, *info;
	void *pos, *entry0, *entry1;
	unsigned int size;
	int ret;

	info = *pinfo;
	entry0 = *pentry0;
	size = total_size;
	info->number = number;

	/* Init all hooks to impossible value. */
	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
		info->hook_entry[i] = 0xFFFFFFFF;
		info->underflow[i] = 0xFFFFFFFF;
	}

	duprintf("translate_compat_table: size %u\n", info->size);
	i = 0;
	xt_compat_lock(AF_INET);
	/* Walk through entries, checking offsets. */
	ret = IPT_ENTRY_ITERATE(entry0, total_size,
				check_compat_entry_size_and_hooks,
				info, &size, entry0,
				entry0 + total_size,
				hook_entries, underflows, &i, name);
	if (ret != 0)
		goto out_unlock;

	ret = -EINVAL;
	if (i != number) {
		duprintf("translate_compat_table: %u not %u entries\n",
			 i, number);
		goto out_unlock;
	}

	/* Check hooks all assigned */
	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
		/* Only hooks which are valid */
		if (!(valid_hooks & (1 << i)))
			continue;
		if (info->hook_entry[i] == 0xFFFFFFFF) {
			duprintf("Invalid hook entry %u %u\n",
				 i, hook_entries[i]);
			goto out_unlock;
		}
		if (info->underflow[i] == 0xFFFFFFFF) {
			duprintf("Invalid underflow %u %u\n",
				 i, underflows[i]);
			goto out_unlock;
		}
	}

	ret = -ENOMEM;
	newinfo = xt_alloc_table_info(size);
	if (!newinfo)
		goto out_unlock;

	newinfo->number = number;
	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
		newinfo->hook_entry[i] = info->hook_entry[i];
		newinfo->underflow[i] = info->underflow[i];
	}
	entry1 = newinfo->entries[raw_smp_processor_id()];
	pos = entry1;
	size =  total_size;
	ret = IPT_ENTRY_ITERATE(entry0, total_size,
			compat_copy_entry_from_user, &pos, &size,
			name, newinfo, entry1);
	compat_flush_offsets();
	xt_compat_unlock(AF_INET);
	if (ret)
		goto free_newinfo;

	ret = -ELOOP;
	if (!mark_source_chains(newinfo, valid_hooks, entry1))
		goto free_newinfo;

	/* And one copy for every other CPU */
	for_each_possible_cpu(i)
		if (newinfo->entries[i] && newinfo->entries[i] != entry1)
			memcpy(newinfo->entries[i], entry1, newinfo->size);

	*pinfo = newinfo;
	*pentry0 = entry1;
	xt_free_table_info(info);
	return 0;

free_newinfo:
	xt_free_table_info(newinfo);
out:
	return ret;
out_unlock:
	xt_compat_unlock(AF_INET);
	goto out;
}


check_match(struct ipt_entry_match *m,
	    const char *name,
	    const struct ipt_ip *ip,
	    unsigned int hookmask,
	    unsigned int *i)
{
	struct ipt_match *match;
	int ret;

	match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
						   m->u.user.revision),
					"ipt_%s", m->u.user.name);
	if (IS_ERR(match) || !match) {
		duprintf("check_match: `%s' not found\n", m->u.user.name);
		return match ? PTR_ERR(match) : -ENOENT;
	}
	m->u.kernel.match = match;

	ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m),
			     name, hookmask, ip->proto,
			     ip->invflags & IPT_INV_PROTO);
	if (ret)
		goto err;

	if (m->u.kernel.match->checkentry
	    && !m->u.kernel.match->checkentry(name, ip, match, m->data,
					      hookmask)) {
		duprintf("ip_tables: check failed for `%s'.\n",
			 m->u.kernel.match->name);
		ret = -EINVAL;
		goto err;
	}

	(*i)++;
	return 0;
err:
	module_put(m->u.kernel.match->me);
	return ret;
}


struct xt_table_info *xt_alloc_table_info(unsigned int size)
{
	struct xt_table_info *newinfo;
	int cpu;

	/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
	if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
		return NULL;

	newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL);
	if (!newinfo)
		return NULL;

	newinfo->size = size;

	for_each_possible_cpu(cpu) {
		if (size <= PAGE_SIZE)
			newinfo->entries[cpu] = kmalloc_node(size,
							GFP_KERNEL,
							cpu_to_node(cpu));
		else
			newinfo->entries[cpu] = vmalloc_node(size,
							cpu_to_node(cpu));

		if (newinfo->entries[cpu] == NULL) {
			xt_free_table_info(newinfo);
			return NULL;
		}
	}

	return newinfo;
}


int xt_check_match(const struct xt_match *match, unsigned short family,
                   unsigned int size, const char *table, unsigned int hook_mask,
		   unsigned short proto, int inv_proto)
{
	if (XT_ALIGN(match->matchsize) != size) {
		printk("%s_tables: %s match: invalid size %Zu != %u\n",
		       xt_prefix[family], match->name,
		       XT_ALIGN(match->matchsize), size);
		return -EINVAL;
	}
	if (match->table && strcmp(match->table, table)) {
		printk("%s_tables: %s match: only valid in %s table, not %s\n",
		       xt_prefix[family], match->name, match->table, table);
		return -EINVAL;
	}
	if (match->hooks && (hook_mask & ~match->hooks) != 0) {
		printk("%s_tables: %s match: bad hook_mask %u\n",
		       xt_prefix[family], match->name, hook_mask);
		return -EINVAL;
	}
	if (match->proto && (match->proto != proto || inv_proto)) {
		printk("%s_tables: %s match: only valid for protocol %u\n",
		       xt_prefix[family], match->name, match->proto);
		return -EINVAL;
	}
	return 0;
}


static void __sco_sock_close(struct sock *sk)
{
	BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);

	switch (sk->sk_state) {
	case BT_LISTEN:
		sco_sock_cleanup_listen(sk);
		break;

	case BT_CONNECTED:
	case BT_CONFIG:
		if (sco_pi(sk)->conn->hcon) {
			sk->sk_state = BT_DISCONN;
			sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
			sco_conn_lock(sco_pi(sk)->conn);
			hci_conn_drop(sco_pi(sk)->conn->hcon);
			sco_pi(sk)->conn->hcon = NULL;
			sco_conn_unlock(sco_pi(sk)->conn);
		} else
			sco_chan_del(sk, ECONNRESET);
		break;

	case BT_CONNECT2:
	case BT_CONNECT:
	case BT_DISCONN:
		sco_chan_del(sk, ECONNRESET);
		break;

	default:
		sock_set_flag(sk, SOCK_ZAPPED);
		break;
	}

}


static void sco_conn_del(struct hci_conn *hcon, int err)
{
	struct sco_conn *conn = hcon->sco_data;
	struct sock *sk;

	if (!conn)
		return;

	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);

	/* Kill socket */
	sco_conn_lock(conn);
	sk = conn->sk;
	if (sk)
		sock_hold(sk);
	sco_conn_unlock(conn);

	if (sk) {
		lock_sock(sk);
		sco_sock_clear_timer(sk);
		sco_chan_del(sk, err);
		release_sock(sk);
		sock_put(sk);
	}

	/* Ensure no more work items will run before freeing conn. */
	cancel_delayed_work_sync(&conn->timeout_work);

	hcon->sco_data = NULL;
	kfree(conn);
}


cockpit_auth_login_async (CockpitAuth *self,
                          const gchar *path,
                          GIOStream *connection,
                          GHashTable *headers,
                          GAsyncReadyCallback callback,
                          gpointer user_data)
{
  g_autoptr(GSimpleAsyncResult) result = NULL;
  CockpitSession *session;
  GError *error = NULL;
  g_autofree gchar *type = NULL;
  g_autofree gchar *conversation = NULL;
  g_autofree gchar *authorization = NULL;
  g_autofree gchar *application = NULL;

  g_return_if_fail (path != NULL);
  g_return_if_fail (headers != NULL);

  self->startups++;

  result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
                                      cockpit_auth_login_async);

  if (!can_start_auth (self))
    {
      g_message ("Request dropped; too many startup connections: %u", self->startups);
      g_simple_async_result_set_error (result, COCKPIT_ERROR, COCKPIT_ERROR_FAILED,
                                       "Connection closed by host");
      g_simple_async_result_complete_in_idle (result);
      goto out;
    }

  application = cockpit_auth_parse_application (path, NULL);

  /* If the client sends a TLS certificate to cockpit-tls, treat this as a
   * definitive login type, and don't just silently fall back to other types */
  if (https_instance_has_certificate_file (NULL, 0) != -1)
    {
      g_debug ("TLS connection has peer certificate, using tls-cert auth type");
      type = g_strdup ("tls-cert");
      /* don't send any actual authorization here; we don't want to put any trust in data sent from cockpit-ws */
      authorization = g_strdup ("tls-cert");
    }
  else
    {
      g_debug ("No peer certificate");
      authorization = cockpit_auth_steal_authorization (headers, connection, &type, &conversation);

      if (!authorization)
        {
          g_simple_async_result_set_error (result, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
                                           "Authentication required");
          g_simple_async_result_complete_in_idle (result);
          goto out;
        }
    }

  if (!application)
    {
      g_simple_async_result_set_error (result, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
                                       "Application required");
      g_simple_async_result_complete_in_idle (result);
      goto out;
    }

  if (conversation)
    {
      session = g_hash_table_lookup (self->conversations, conversation);
      if (!session)
        {
          g_simple_async_result_set_error (result, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
                                           "Invalid conversation token");
          g_simple_async_result_complete_in_idle (result);
          goto out;
        }

      g_simple_async_result_set_op_res_gpointer (result, cockpit_session_ref (session), cockpit_session_unref);
    }
  else
    {
      session = cockpit_session_launch (self, connection, headers, type, authorization, application, &error);
      if (!session)
        {
          g_simple_async_result_take_error (result, error);
          g_simple_async_result_complete_in_idle (result);
          goto out;
        }
      g_simple_async_result_set_op_res_gpointer (result, session, cockpit_session_unref);
    }

  cockpit_session_reset (session);
  session->result = g_object_ref (result);

  session->authorization = authorization;
  authorization = NULL;

  if (conversation && !reply_authorize_challenge (session))
    {
      g_simple_async_result_set_error (result, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
                                       "Invalid conversation reply");
      g_simple_async_result_complete_in_idle (result);
      goto out;
    }

  reset_authorize_timeout (session, FALSE);

out:
  if (authorization)
    cockpit_memory_clear (authorization, -1);
}


reply_authorize_challenge (CockpitSession *session)
{
  const gchar *challenge = NULL;
  char *authorize_type = NULL;
  char *authorization_type = NULL;
  const gchar *cookie = NULL;
  const gchar *response = NULL;
  JsonObject *login_data = NULL;
  gboolean ret = FALSE;

  if (!session->authorize)
    goto out;

  if (!cockpit_json_get_string (session->authorize, "cookie", NULL, &cookie) ||
      !cockpit_json_get_string (session->authorize, "challenge", NULL, &challenge) ||
      !cockpit_json_get_string (session->authorize, "response", NULL, &response))
    goto out;

  if (response && !cookie)
    {
      if (session->authorization)
        {
          cockpit_memory_clear (session->authorization, -1);
          g_free (session->authorization);
        }

      session->authorization = g_strdup (response);
      ret = TRUE;
      goto out;
    }

  if (!challenge || !cookie)
    goto out;

  if (!cockpit_authorize_type (challenge, &authorize_type))
    goto out;

  /* Handle prompting for login data */
  if (g_str_equal (authorize_type, "x-login-data"))
    {
      if (cockpit_json_get_object (session->authorize, "login-data", NULL, &login_data) && login_data)
        cockpit_creds_set_login_data (cockpit_web_service_get_creds (session->service), login_data);
      ret = TRUE;
      goto out;
    }

  if (!session->authorization)
    goto out;

  if (cockpit_authorize_type (session->authorization, &authorization_type) &&
      (g_str_equal (authorize_type, "*") || g_str_equal (authorize_type, authorization_type)))
    {
      send_authorize_reply (session->transport, cookie, session->authorization);
      cockpit_memory_clear (session->authorization, -1);
      g_free (session->authorization);
      session->authorization = NULL;
      ret = TRUE;
    }

out:
  free (authorize_type);
  free (authorization_type);
  return ret;
}


cmyk2hsv(UINT8 *out, const UINT8 *in, int xsize) {
    int x, nk, tmp;
    for (x = 0; x < xsize; x++) {
        nk = 255 - in[3];
        out[0] = CLIP8(nk - MULDIV255(in[0], nk, tmp));
        out[1] = CLIP8(nk - MULDIV255(in[1], nk, tmp));
        out[2] = CLIP8(nk - MULDIV255(in[2], nk, tmp));
        rgb2hsv_row(out, out);
        out[3] = 255;
        out += 4;
        in += 4;
    }
}


la2hsv(UINT8 *out, const UINT8 *in, int xsize) {
    int x;
    for (x = 0; x < xsize; x++, in += 4, out += 4) {
        UINT8 v = in[0];
        out[0] = 0;
        out[1] = 0;
        out[2] = v;
        out[3] = in[3];
    }
}


rgb2i(UINT8 *out_, const UINT8 *in, int xsize) {
    int x;
    for (x = 0; x < xsize; x++, in += 4, out_ += 4) {
        INT32 v = L24(in) >> 16;
        memcpy(out_, &v, sizeof(v));
    }
}


rgba2la(UINT8 *out, const UINT8 *in, int xsize) {
    int x;
    for (x = 0; x < xsize; x++, in += 4, out += 4) {
        /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
        out[0] = out[1] = out[2] = L24(in) >> 16;
        out[3] = in[3];
    }
}


pa2bit(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) {
    int x;
    /* FIXME: precalculate greyscale palette? */
    for (x = 0; x < xsize; x++, in += 4) {
        *out++ = (L(&palette[in[0] * 4]) >= 128000) ? 255 : 0;
    }
}


p2rgba(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) {
    int x;
    for (x = 0; x < xsize; x++) {
        const UINT8 *rgba = &palette[*in++ * 4];
        *out++ = rgba[0];
        *out++ = rgba[1];
        *out++ = rgba[2];
        *out++ = rgba[3];
    }
}


I_I16L(UINT8 *out, const UINT8 *in_, int xsize) {
    int x, v;
    for (x = 0; x < xsize; x++, in_ += 4) {
        INT32 i;
        memcpy(&i, in_, sizeof(i));
        v = CLIP16(i);
        *out++ = (UINT8)v;
        *out++ = (UINT8)(v >> 8);
    }
}


la2cmyk(UINT8 *out, const UINT8 *in, int xsize) {
    int x;
    for (x = 0; x < xsize; x++, in += 4) {
        *out++ = 0;
        *out++ = 0;
        *out++ = 0;
        *out++ = ~(in[0]);
    }
}


p2hsv(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) {
    int x;
    for (x = 0; x < xsize; x++, out += 4) {
        const UINT8 *rgb = &palette[*in++ * 4];
        rgb2hsv_row(out, rgb);
        out[3] = 255;
    }
}


h2_frame_complete(struct http_conn *htc)
{
	struct h2_sess *h2;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	CAST_OBJ_NOTNULL(h2, htc->priv, H2_SESS_MAGIC);
	if (htc->rxbuf_b + 9 > htc->rxbuf_e ||
	    htc->rxbuf_b + 9 + (vbe32dec(htc->rxbuf_b) >> 8) > htc->rxbuf_e)
		return (HTC_S_MORE);
	return (HTC_S_COMPLETE);
}


h2_sweep(struct worker *wrk, struct h2_sess *h2)
{
	int tmo = 0;
	struct h2_req *r2, *r22;

	ASSERT_RXTHR(h2);

	h2->do_sweep = 0;
	VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
		if (r2 == h2->req0) {
			assert (r2->state == H2_S_IDLE);
			continue;
		}
		switch (r2->state) {
		case H2_S_CLOSED:
			if (!r2->scheduled)
				h2_del_req(wrk, r2);
			break;
		case H2_S_CLOS_REM:
			if (!r2->scheduled) {
				H2_Send_Get(wrk, h2, h2->req0);
				H2_Send_RST(wrk, h2, h2->req0, r2->stream,
				    H2SE_REFUSED_STREAM);
				H2_Send_Rel(h2, h2->req0);
				h2_del_req(wrk, r2);
				continue;
			}
			/* FALLTHROUGH */
		case H2_S_CLOS_LOC:
		case H2_S_OPEN:
			if (h2_stream_tmo_unlocked(h2, r2)) {
				tmo = 1;
				continue;
			}
			break;
		case H2_S_IDLE:
			/* Current code make this unreachable: h2_new_req is
			 * only called inside h2_rx_headers, which immediately
			 * sets the new stream state to H2_S_OPEN */
			/* FALLTHROUGH */
		default:
			WRONG("Wrong h2 stream state");
			break;
		}
	}
	if (tmo)
		return (0);
	return (h2->refcnt > 1);
}


h2_end_headers(struct worker *wrk, struct h2_sess *h2,
    struct req *req, struct h2_req *r2)
{
	h2_error h2e;
	ssize_t cl;

	ASSERT_RXTHR(h2);
	assert(r2->state == H2_S_OPEN);
	h2e = h2h_decode_fini(h2);
	h2->new_req = NULL;
	if (h2e != NULL) {
		Lck_Lock(&h2->sess->mtx);
		VSLb(h2->vsl, SLT_Debug, "HPACK/FINI %s", h2e->name);
		Lck_Unlock(&h2->sess->mtx);
		assert(!WS_IsReserved(r2->req->ws));
		h2_del_req(wrk, r2);
		return (h2e);
	}
	VSLb_ts_req(req, "Req", req->t_req);

	// XXX: Smarter to do this already at HPACK time into tail end of
	// XXX: WS, then copy back once all headers received.
	// XXX: Have I mentioned H/2 Is hodge-podge ?
	http_CollectHdrSep(req->http, H_Cookie, "; ");	// rfc7540,l,3114,3120

	cl = http_GetContentLength(req->http);
	assert(cl >= -2);
	if (cl == -2) {
		VSLb(h2->vsl, SLT_Debug, "Non-parseable Content-Length");
		return (H2SE_PROTOCOL_ERROR);
	}

	if (req->req_body_status == NULL) {
		if (cl == -1)
			req->req_body_status = BS_EOF;
		else
			req->req_body_status = BS_LENGTH;
		req->htc->content_length = cl;
	} else {
		/* A HEADER frame contained END_STREAM */
		assert (req->req_body_status == BS_NONE);
		r2->state = H2_S_CLOS_REM;
		if (cl > 0)
			return (H2CE_PROTOCOL_ERROR); //rfc7540,l,1838,1840
	}

	if (req->http->hd[HTTP_HDR_METHOD].b == NULL) {
		VSLb(h2->vsl, SLT_Debug, "Missing :method");
		return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
	}
	if (req->http->hd[HTTP_HDR_URL].b == NULL) {
		VSLb(h2->vsl, SLT_Debug, "Missing :path");
		return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
	}
	AN(req->http->hd[HTTP_HDR_PROTO].b);

	assert(req->req_step == R_STP_TRANSPORT);
	VCL_TaskEnter(req->privs);
	VCL_TaskEnter(req->top->privs);
	req->task->func = h2_do_req;
	req->task->priv = req;
	r2->scheduled = 1;
	if (Pool_Task(wrk->pool, req->task, TASK_QUEUE_STR) != 0) {
		r2->scheduled = 0;
		r2->state = H2_S_CLOSED;
		return (H2SE_REFUSED_STREAM); //rfc7540,l,3326,3329
	}
	return (0);
}


h2_vfp_body_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
{
	struct h2_req *r2;
	struct h2_sess *h2;

	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
	CAST_OBJ_NOTNULL(r2, vfe->priv1, H2_REQ_MAGIC);
	CHECK_OBJ_NOTNULL(r2->req, REQ_MAGIC);
	h2 = r2->h2sess;

	if (vc->failed) {
		CHECK_OBJ_NOTNULL(r2->req->wrk, WORKER_MAGIC);
		H2_Send_Get(r2->req->wrk, h2, r2);
		H2_Send_RST(r2->req->wrk, h2, r2, r2->stream,
		    H2SE_REFUSED_STREAM);
		H2_Send_Rel(h2, r2);
		Lck_Lock(&h2->sess->mtx);
		r2->error = H2SE_REFUSED_STREAM;
		if (h2->mailcall == r2) {
			h2->mailcall = NULL;
			AZ(pthread_cond_signal(h2->cond));
		}
		Lck_Unlock(&h2->sess->mtx);
	}
}


h2_rx_continuation(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{
	struct req *req;
	h2_error h2e;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	ASSERT_RXTHR(h2);
	CHECK_OBJ_ORNULL(r2, H2_REQ_MAGIC);

	if (r2 == NULL || r2->state != H2_S_OPEN || r2->req != h2->new_req)
		return (H2CE_PROTOCOL_ERROR);	// XXX spec ?
	req = r2->req;
	h2e = h2h_decode_bytes(h2, h2->rxf_data, h2->rxf_len);
	r2->req->acct.req_hdrbytes += h2->rxf_len;
	if (h2e != NULL) {
		Lck_Lock(&h2->sess->mtx);
		VSLb(h2->vsl, SLT_Debug, "HPACK(cont) %s", h2e->name);
		Lck_Unlock(&h2->sess->mtx);
		(void)h2h_decode_fini(h2);
		assert(!WS_IsReserved(r2->req->ws));
		h2_del_req(wrk, r2);
		return (h2e);
	}
	if (h2->rxf_flags & H2FF_HEADERS_END_HEADERS)
		return (h2_end_headers(wrk, h2, req, r2));
	return (0);
}


crypt_pw_cmp(const char *userpwd, const char *dbpwd)
{
    int rc = -1;
    char *cp = NULL;
    size_t dbpwd_len = strlen(dbpwd);
    struct crypt_data data;
    data.initialized = 0;

    /*
     * there MUST be at least 2 chars of salt and some pw bytes, else this is INVALID and will
     * allow any password to bind as we then only compare SALTS.
     */
    if (dbpwd_len >= 3) {
        /* we use salt (first 2 chars) of encoded password in call to crypt_r() */
        cp = crypt_r(userpwd, dbpwd, &data);
    }
    /* If these are not the same length, we can not proceed safely with memcmp. */
    if (cp && dbpwd_len == strlen(cp)) {
        rc = slapi_ct_memcmp(dbpwd, cp, dbpwd_len);
    } else {
        rc = -1;
    }
    return rc;
}


static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	u32 dummy;
	u32 eax = 1;

	svm->spec_ctrl = 0;
	svm->virt_spec_ctrl = 0;

	if (!init_event) {
		vcpu->arch.apic_base = APIC_DEFAULT_PHYS_BASE |
				       MSR_IA32_APICBASE_ENABLE;
		if (kvm_vcpu_is_reset_bsp(vcpu))
			vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP;
	}
	init_vmcb(vcpu);

	kvm_cpuid(vcpu, &eax, &dummy, &dummy, &dummy, false);
	kvm_rdx_write(vcpu, eax);

	if (kvm_vcpu_apicv_active(vcpu) && !init_event)
		avic_update_vapic_bar(svm, APIC_DEFAULT_PHYS_BASE);
}


static int svm_hardware_enable(void)
{

	struct svm_cpu_data *sd;
	uint64_t efer;
	struct desc_struct *gdt;
	int me = raw_smp_processor_id();

	rdmsrl(MSR_EFER, efer);
	if (efer & EFER_SVME)
		return -EBUSY;

	if (!has_svm()) {
		pr_err("%s: err EOPNOTSUPP on %d\n", __func__, me);
		return -EINVAL;
	}
	sd = per_cpu(svm_data, me);
	if (!sd) {
		pr_err("%s: svm_data is NULL on %d\n", __func__, me);
		return -EINVAL;
	}

	sd->asid_generation = 1;
	sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
	sd->next_asid = sd->max_asid + 1;
	sd->min_asid = max_sev_asid + 1;

	gdt = get_current_gdt_rw();
	sd->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);

	wrmsrl(MSR_EFER, efer | EFER_SVME);

	wrmsrl(MSR_VM_HSAVE_PA, __sme_page_pa(sd->save_area));

	if (static_cpu_has(X86_FEATURE_TSCRATEMSR)) {
		wrmsrl(MSR_AMD64_TSC_RATIO, TSC_RATIO_DEFAULT);
		__this_cpu_write(current_tsc_ratio, TSC_RATIO_DEFAULT);
	}

	/*
	 * Get OSVW bits.
	 *
	 * Note that it is possible to have a system with mixed processor
	 * revisions and therefore different OSVW bits. If bits are not the same
	 * on different processors then choose the worst case (i.e. if erratum
	 * is present on one processor and not on another then assume that the
	 * erratum is present everywhere).
	 */
	if (cpu_has(&boot_cpu_data, X86_FEATURE_OSVW)) {
		uint64_t len, status = 0;
		int err;

		len = native_read_msr_safe(MSR_AMD64_OSVW_ID_LENGTH, &err);
		if (!err)
			status = native_read_msr_safe(MSR_AMD64_OSVW_STATUS,
						      &err);

		if (err)
			osvw_status = osvw_len = 0;
		else {
			if (len < osvw_len)
				osvw_len = len;
			osvw_status |= status;
			osvw_status &= (1ULL << osvw_len) - 1;
		}
	} else
		osvw_status = osvw_len = 0;

	svm_init_erratum_383();

	amd_pmu_enable_virt();

	return 0;
}


static __init void svm_adjust_mmio_mask(void)
{
	unsigned int enc_bit, mask_bit;
	u64 msr, mask;

	/* If there is no memory encryption support, use existing mask */
	if (cpuid_eax(0x80000000) < 0x8000001f)
		return;

	/* If memory encryption is not enabled, use existing mask */
	rdmsrl(MSR_AMD64_SYSCFG, msr);
	if (!(msr & MSR_AMD64_SYSCFG_MEM_ENCRYPT))
		return;

	enc_bit = cpuid_ebx(0x8000001f) & 0x3f;
	mask_bit = boot_cpu_data.x86_phys_bits;

	/* Increment the mask bit if it is the same as the encryption bit */
	if (enc_bit == mask_bit)
		mask_bit++;

	/*
	 * If the mask bit location is below 52, then some bits above the
	 * physical addressing limit will always be reserved, so use the
	 * rsvd_bits() function to generate the mask. This mask, along with
	 * the present bit, will be used to generate a page fault with
	 * PFER.RSV = 1.
	 *
	 * If the mask bit location is 52 (or above), then clear the mask.
	 */
	mask = (mask_bit < 52) ? rsvd_bits(mask_bit, 51) | PT_PRESENT_MASK : 0;

	kvm_mmu_set_mmio_spte_mask(mask, mask, PT_WRITABLE_MASK | PT_USER_MASK);
}


static __init int svm_hardware_setup(void)
{
	int cpu;
	struct page *iopm_pages;
	void *iopm_va;
	int r;
	unsigned int order = get_order(IOPM_SIZE);

	/*
	 * NX is required for shadow paging and for NPT if the NX huge pages
	 * mitigation is enabled.
	 */
	if (!boot_cpu_has(X86_FEATURE_NX)) {
		pr_err_ratelimited("NX (Execute Disable) not supported\n");
		return -EOPNOTSUPP;
	}
	kvm_enable_efer_bits(EFER_NX);

	iopm_pages = alloc_pages(GFP_KERNEL, order);

	if (!iopm_pages)
		return -ENOMEM;

	iopm_va = page_address(iopm_pages);
	memset(iopm_va, 0xff, PAGE_SIZE * (1 << order));
	iopm_base = page_to_pfn(iopm_pages) << PAGE_SHIFT;

	init_msrpm_offsets();

	supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);

	if (boot_cpu_has(X86_FEATURE_FXSR_OPT))
		kvm_enable_efer_bits(EFER_FFXSR);

	if (boot_cpu_has(X86_FEATURE_TSCRATEMSR)) {
		kvm_has_tsc_control = true;
		kvm_max_tsc_scaling_ratio = TSC_RATIO_MAX;
		kvm_tsc_scaling_ratio_frac_bits = 32;
	}

	tsc_aux_uret_slot = kvm_add_user_return_msr(MSR_TSC_AUX);

	/* Check for pause filtering support */
	if (!boot_cpu_has(X86_FEATURE_PAUSEFILTER)) {
		pause_filter_count = 0;
		pause_filter_thresh = 0;
	} else if (!boot_cpu_has(X86_FEATURE_PFTHRESHOLD)) {
		pause_filter_thresh = 0;
	}

	if (nested) {
		printk(KERN_INFO "kvm: Nested Virtualization enabled\n");
		kvm_enable_efer_bits(EFER_SVME | EFER_LMSLE);
	}

	/*
	 * KVM's MMU doesn't support using 2-level paging for itself, and thus
	 * NPT isn't supported if the host is using 2-level paging since host
	 * CR4 is unchanged on VMRUN.
	 */
	if (!IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_X86_PAE))
		npt_enabled = false;

	if (!boot_cpu_has(X86_FEATURE_NPT))
		npt_enabled = false;

	kvm_configure_mmu(npt_enabled, get_max_npt_level(), PG_LEVEL_1G);
	pr_info("kvm: Nested Paging %sabled\n", npt_enabled ? "en" : "dis");

	/* Note, SEV setup consumes npt_enabled. */
	sev_hardware_setup();

	svm_hv_hardware_setup();

	svm_adjust_mmio_mask();

	for_each_possible_cpu(cpu) {
		r = svm_cpu_init(cpu);
		if (r)
			goto err;
	}

	if (nrips) {
		if (!boot_cpu_has(X86_FEATURE_NRIPS))
			nrips = false;
	}

	enable_apicv = avic = avic && npt_enabled && boot_cpu_has(X86_FEATURE_AVIC);

	if (enable_apicv) {
		pr_info("AVIC enabled\n");

		amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier);
	}

	if (vls) {
		if (!npt_enabled ||
		    !boot_cpu_has(X86_FEATURE_V_VMSAVE_VMLOAD) ||
		    !IS_ENABLED(CONFIG_X86_64)) {
			vls = false;
		} else {
			pr_info("Virtual VMLOAD VMSAVE supported\n");
		}
	}

	if (boot_cpu_has(X86_FEATURE_SVME_ADDR_CHK))
		svm_gp_erratum_intercept = false;

	if (vgif) {
		if (!boot_cpu_has(X86_FEATURE_VGIF))
			vgif = false;
		else
			pr_info("Virtual GIF supported\n");
	}

	svm_set_cpu_caps();

	/*
	 * It seems that on AMD processors PTE's accessed bit is
	 * being set by the CPU hardware before the NPF vmexit.
	 * This is not expected behaviour and our tests fail because
	 * of it.
	 * A workaround here is to disable support for
	 * GUEST_MAXPHYADDR < HOST_MAXPHYADDR if NPT is enabled.
	 * In this case userspace can know if there is support using
	 * KVM_CAP_SMALLER_MAXPHYADDR extension and decide how to handle
	 * it
	 * If future AMD CPU models change the behaviour described above,
	 * this variable can be changed accordingly
	 */
	allow_smaller_maxphyaddr = !npt_enabled;

	return 0;

err:
	svm_hardware_teardown();
	return r;
}


static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);

	trace_kvm_entry(vcpu);

	svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
	svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
	svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP];

	/*
	 * Disable singlestep if we're injecting an interrupt/exception.
	 * We don't want our modified rflags to be pushed on the stack where
	 * we might not be able to easily reset them if we disabled NMI
	 * singlestep later.
	 */
	if (svm->nmi_singlestep && svm->vmcb->control.event_inj) {
		/*
		 * Event injection happens before external interrupts cause a
		 * vmexit and interrupts are disabled here, so smp_send_reschedule
		 * is enough to force an immediate vmexit.
		 */
		disable_nmi_singlestep(svm);
		smp_send_reschedule(vcpu->cpu);
	}

	pre_svm_run(vcpu);

	sync_lapic_to_cr8(vcpu);

	if (unlikely(svm->asid != svm->vmcb->control.asid)) {
		svm->vmcb->control.asid = svm->asid;
		vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
	}
	svm->vmcb->save.cr2 = vcpu->arch.cr2;

	svm_hv_update_vp_id(svm->vmcb, vcpu);

	/*
	 * Run with all-zero DR6 unless needed, so that we can get the exact cause
	 * of a #DB.
	 */
	if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))
		svm_set_dr6(svm, vcpu->arch.dr6);
	else
		svm_set_dr6(svm, DR6_ACTIVE_LOW);

	clgi();
	kvm_load_guest_xsave_state(vcpu);

	kvm_wait_lapic_expire(vcpu);

	/*
	 * If this vCPU has touched SPEC_CTRL, restore the guest's value if
	 * it's non-zero. Since vmentry is serialising on affected CPUs, there
	 * is no need to worry about the conditional branch over the wrmsr
	 * being speculatively taken.
	 */
	if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL))
		x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl);

	svm_vcpu_enter_exit(vcpu);

	/*
	 * We do not use IBRS in the kernel. If this vCPU has used the
	 * SPEC_CTRL MSR it may have left it on; save the value and
	 * turn it off. This is much more efficient than blindly adding
	 * it to the atomic save/restore list. Especially as the former
	 * (Saving guest MSRs on vmexit) doesn't even exist in KVM.
	 *
	 * For non-nested case:
	 * If the L01 MSR bitmap does not intercept the MSR, then we need to
	 * save it.
	 *
	 * For nested case:
	 * If the L02 MSR bitmap does not intercept the MSR, then we need to
	 * save it.
	 */
	if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL) &&
	    unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
		svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);

	if (!sev_es_guest(vcpu->kvm))
		reload_tss(vcpu);

	if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL))
		x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl);

	if (!sev_es_guest(vcpu->kvm)) {
		vcpu->arch.cr2 = svm->vmcb->save.cr2;
		vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
		vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
		vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
	}

	if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI))
		kvm_before_interrupt(vcpu);

	kvm_load_host_xsave_state(vcpu);
	stgi();

	/* Any pending NMI will happen here */

	if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI))
		kvm_after_interrupt(vcpu);

	sync_cr8_to_lapic(vcpu);

	svm->next_rip = 0;
	if (is_guest_mode(vcpu)) {
		nested_sync_control_from_vmcb02(svm);

		/* Track VMRUNs that have made past consistency checking */
		if (svm->nested.nested_run_pending &&
		    svm->vmcb->control.exit_code != SVM_EXIT_ERR)
                        ++vcpu->stat.nested_run;

		svm->nested.nested_run_pending = 0;
	}

	svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
	vmcb_mark_all_clean(svm->vmcb);

	/* if exit due to PF check for async PF */
	if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR)
		vcpu->arch.apf.host_apf_flags =
			kvm_read_and_reset_apf_flags();

	if (npt_enabled)
		kvm_register_clear_available(vcpu, VCPU_EXREG_PDPTR);

	/*
	 * We need to handle MC intercepts here before the vcpu has a chance to
	 * change the physical cpu
	 */
	if (unlikely(svm->vmcb->control.exit_code ==
		     SVM_EXIT_EXCP_BASE + MC_VECTOR))
		svm_handle_mce(vcpu);

	svm_complete_interrupts(vcpu);

	if (is_guest_mode(vcpu))
		return EXIT_FASTPATH_NONE;

	return svm_exit_handlers_fastpath(vcpu);
}


static bool svm_get_nmi_mask(struct kvm_vcpu *vcpu)
{
	return !!(vcpu->arch.hflags & HF_NMI_MASK);
}


static int invpcid_interception(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	unsigned long type;
	gva_t gva;

	if (!guest_cpuid_has(vcpu, X86_FEATURE_INVPCID)) {
		kvm_queue_exception(vcpu, UD_VECTOR);
		return 1;
	}

	/*
	 * For an INVPCID intercept:
	 * EXITINFO1 provides the linear address of the memory operand.
	 * EXITINFO2 provides the contents of the register operand.
	 */
	type = svm->vmcb->control.exit_info_2;
	gva = svm->vmcb->control.exit_info_1;

	if (type > 3) {
		kvm_inject_gp(vcpu, 0);
		return 1;
	}

	return kvm_handle_invpcid(vcpu, type, gva);
}


static int svm_check_intercept(struct kvm_vcpu *vcpu,
			       struct x86_instruction_info *info,
			       enum x86_intercept_stage stage,
			       struct x86_exception *exception)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	int vmexit, ret = X86EMUL_CONTINUE;
	struct __x86_intercept icpt_info;
	struct vmcb *vmcb = svm->vmcb;

	if (info->intercept >= ARRAY_SIZE(x86_intercept_map))
		goto out;

	icpt_info = x86_intercept_map[info->intercept];

	if (stage != icpt_info.stage)
		goto out;

	switch (icpt_info.exit_code) {
	case SVM_EXIT_READ_CR0:
		if (info->intercept == x86_intercept_cr_read)
			icpt_info.exit_code += info->modrm_reg;
		break;
	case SVM_EXIT_WRITE_CR0: {
		unsigned long cr0, val;

		if (info->intercept == x86_intercept_cr_write)
			icpt_info.exit_code += info->modrm_reg;

		if (icpt_info.exit_code != SVM_EXIT_WRITE_CR0 ||
		    info->intercept == x86_intercept_clts)
			break;

		if (!(vmcb_is_intercept(&svm->nested.ctl,
					INTERCEPT_SELECTIVE_CR0)))
			break;

		cr0 = vcpu->arch.cr0 & ~SVM_CR0_SELECTIVE_MASK;
		val = info->src_val  & ~SVM_CR0_SELECTIVE_MASK;

		if (info->intercept == x86_intercept_lmsw) {
			cr0 &= 0xfUL;
			val &= 0xfUL;
			/* lmsw can't clear PE - catch this here */
			if (cr0 & X86_CR0_PE)
				val |= X86_CR0_PE;
		}

		if (cr0 ^ val)
			icpt_info.exit_code = SVM_EXIT_CR0_SEL_WRITE;

		break;
	}
	case SVM_EXIT_READ_DR0:
	case SVM_EXIT_WRITE_DR0:
		icpt_info.exit_code += info->modrm_reg;
		break;
	case SVM_EXIT_MSR:
		if (info->intercept == x86_intercept_wrmsr)
			vmcb->control.exit_info_1 = 1;
		else
			vmcb->control.exit_info_1 = 0;
		break;
	case SVM_EXIT_PAUSE:
		/*
		 * We get this for NOP only, but pause
		 * is rep not, check this here
		 */
		if (info->rep_prefix != REPE_PREFIX)
			goto out;
		break;
	case SVM_EXIT_IOIO: {
		u64 exit_info;
		u32 bytes;

		if (info->intercept == x86_intercept_in ||
		    info->intercept == x86_intercept_ins) {
			exit_info = ((info->src_val & 0xffff) << 16) |
				SVM_IOIO_TYPE_MASK;
			bytes = info->dst_bytes;
		} else {
			exit_info = (info->dst_val & 0xffff) << 16;
			bytes = info->src_bytes;
		}

		if (info->intercept == x86_intercept_outs ||
		    info->intercept == x86_intercept_ins)
			exit_info |= SVM_IOIO_STR_MASK;

		if (info->rep_prefix)
			exit_info |= SVM_IOIO_REP_MASK;

		bytes = min(bytes, 4u);

		exit_info |= bytes << SVM_IOIO_SIZE_SHIFT;

		exit_info |= (u32)info->ad_bytes << (SVM_IOIO_ASIZE_SHIFT - 1);

		vmcb->control.exit_info_1 = exit_info;
		vmcb->control.exit_info_2 = info->next_rip;

		break;
	}
	default:
		break;
	}

	/* TODO: Advertise NRIPS to guest hypervisor unconditionally */
	if (static_cpu_has(X86_FEATURE_NRIPS))
		vmcb->control.next_rip  = info->next_rip;
	vmcb->control.exit_code = icpt_info.exit_code;
	vmexit = nested_svm_exit_handled(svm);

	ret = (vmexit == NESTED_EXIT_DONE) ? X86EMUL_INTERCEPTED
					   : X86EMUL_CONTINUE;

out:
	return ret;
}


static void svm_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
			     int root_level)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	unsigned long cr3;

	if (npt_enabled) {
		svm->vmcb->control.nested_cr3 = __sme_set(root_hpa);
		vmcb_mark_dirty(svm->vmcb, VMCB_NPT);

		hv_track_root_tdp(vcpu, root_hpa);

		/* Loading L2's CR3 is handled by enter_svm_guest_mode.  */
		if (!test_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail))
			return;
		cr3 = vcpu->arch.cr3;
	} else if (vcpu->arch.mmu->shadow_root_level >= PT64_ROOT_4LEVEL) {
		cr3 = __sme_set(root_hpa) | kvm_get_active_pcid(vcpu);
	} else {
		/* PCID in the guest should be impossible with a 32-bit MMU. */
		WARN_ON_ONCE(kvm_get_active_pcid(vcpu));
		cr3 = root_hpa;
	}

	svm->vmcb->save.cr3 = cr3;
	vmcb_mark_dirty(svm->vmcb, VMCB_CR);
}


static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr)
{
	u8 bit_write;
	unsigned long tmp;
	u32 offset;
	u32 *msrpm;

	msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm:
				      to_svm(vcpu)->msrpm;

	offset    = svm_msrpm_offset(msr);
	bit_write = 2 * (msr & 0x0f) + 1;
	tmp       = msrpm[offset];

	BUG_ON(offset == MSR_INVALID);

	return !!test_bit(bit_write,  &tmp);
}


static int gp_interception(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	u32 error_code = svm->vmcb->control.exit_info_1;
	int opcode;

	/* Both #GP cases have zero error_code */
	if (error_code)
		goto reinject;

	/* Decode the instruction for usage later */
	if (x86_decode_emulated_instruction(vcpu, 0, NULL, 0) != EMULATION_OK)
		goto reinject;

	opcode = svm_instr_opcode(vcpu);

	if (opcode == NONE_SVM_INSTR) {
		if (!enable_vmware_backdoor)
			goto reinject;

		/*
		 * VMware backdoor emulation on #GP interception only handles
		 * IN{S}, OUT{S}, and RDPMC.
		 */
		if (!is_guest_mode(vcpu))
			return kvm_emulate_instruction(vcpu,
				EMULTYPE_VMWARE_GP | EMULTYPE_NO_DECODE);
	} else
		return emulate_svm_instr(vcpu, opcode);

reinject:
	kvm_queue_exception_e(vcpu, GP_VECTOR, error_code);
	return 1;
}


static int has_svm(void)
{
	const char *msg;

	if (!cpu_has_svm(&msg)) {
		printk(KERN_INFO "has_svm: %s\n", msg);
		return 0;
	}

	if (sev_active()) {
		pr_info("KVM is unsupported when running as an SEV guest\n");
		return 0;
	}

	if (pgtable_l5_enabled()) {
		pr_info("KVM doesn't yet support 5-level paging on AMD SVM\n");
		return 0;
	}

	return 1;
}


static int shutdown_interception(struct kvm_vcpu *vcpu)
{
	struct kvm_run *kvm_run = vcpu->run;
	struct vcpu_svm *svm = to_svm(vcpu);

	/*
	 * The VM save area has already been encrypted so it
	 * cannot be reinitialized - just terminate.
	 */
	if (sev_es_guest(vcpu->kvm))
		return -EINVAL;

	/*
	 * VMCB is undefined after a SHUTDOWN intercept
	 * so reinitialize it.
	 */
	clear_page(svm->vmcb);
	init_vmcb(vcpu);

	kvm_run->exit_reason = KVM_EXIT_SHUTDOWN;
	return 0;
}


static void init_vmcb(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	struct vmcb_control_area *control = &svm->vmcb->control;
	struct vmcb_save_area *save = &svm->vmcb->save;

	vcpu->arch.hflags = 0;

	svm_set_intercept(svm, INTERCEPT_CR0_READ);
	svm_set_intercept(svm, INTERCEPT_CR3_READ);
	svm_set_intercept(svm, INTERCEPT_CR4_READ);
	svm_set_intercept(svm, INTERCEPT_CR0_WRITE);
	svm_set_intercept(svm, INTERCEPT_CR3_WRITE);
	svm_set_intercept(svm, INTERCEPT_CR4_WRITE);
	if (!kvm_vcpu_apicv_active(vcpu))
		svm_set_intercept(svm, INTERCEPT_CR8_WRITE);

	set_dr_intercepts(svm);

	set_exception_intercept(svm, PF_VECTOR);
	set_exception_intercept(svm, UD_VECTOR);
	set_exception_intercept(svm, MC_VECTOR);
	set_exception_intercept(svm, AC_VECTOR);
	set_exception_intercept(svm, DB_VECTOR);
	/*
	 * Guest access to VMware backdoor ports could legitimately
	 * trigger #GP because of TSS I/O permission bitmap.
	 * We intercept those #GP and allow access to them anyway
	 * as VMware does.
	 */
	if (enable_vmware_backdoor)
		set_exception_intercept(svm, GP_VECTOR);

	svm_set_intercept(svm, INTERCEPT_INTR);
	svm_set_intercept(svm, INTERCEPT_NMI);

	if (intercept_smi)
		svm_set_intercept(svm, INTERCEPT_SMI);

	svm_set_intercept(svm, INTERCEPT_SELECTIVE_CR0);
	svm_set_intercept(svm, INTERCEPT_RDPMC);
	svm_set_intercept(svm, INTERCEPT_CPUID);
	svm_set_intercept(svm, INTERCEPT_INVD);
	svm_set_intercept(svm, INTERCEPT_INVLPG);
	svm_set_intercept(svm, INTERCEPT_INVLPGA);
	svm_set_intercept(svm, INTERCEPT_IOIO_PROT);
	svm_set_intercept(svm, INTERCEPT_MSR_PROT);
	svm_set_intercept(svm, INTERCEPT_TASK_SWITCH);
	svm_set_intercept(svm, INTERCEPT_SHUTDOWN);
	svm_set_intercept(svm, INTERCEPT_VMRUN);
	svm_set_intercept(svm, INTERCEPT_VMMCALL);
	svm_set_intercept(svm, INTERCEPT_VMLOAD);
	svm_set_intercept(svm, INTERCEPT_VMSAVE);
	svm_set_intercept(svm, INTERCEPT_STGI);
	svm_set_intercept(svm, INTERCEPT_CLGI);
	svm_set_intercept(svm, INTERCEPT_SKINIT);
	svm_set_intercept(svm, INTERCEPT_WBINVD);
	svm_set_intercept(svm, INTERCEPT_XSETBV);
	svm_set_intercept(svm, INTERCEPT_RDPRU);
	svm_set_intercept(svm, INTERCEPT_RSM);

	if (!kvm_mwait_in_guest(vcpu->kvm)) {
		svm_set_intercept(svm, INTERCEPT_MONITOR);
		svm_set_intercept(svm, INTERCEPT_MWAIT);
	}

	if (!kvm_hlt_in_guest(vcpu->kvm))
		svm_set_intercept(svm, INTERCEPT_HLT);

	control->iopm_base_pa = __sme_set(iopm_base);
	control->msrpm_base_pa = __sme_set(__pa(svm->msrpm));
	control->int_ctl = V_INTR_MASKING_MASK;

	init_seg(&save->es);
	init_seg(&save->ss);
	init_seg(&save->ds);
	init_seg(&save->fs);
	init_seg(&save->gs);

	save->cs.selector = 0xf000;
	save->cs.base = 0xffff0000;
	/* Executable/Readable Code Segment */
	save->cs.attrib = SVM_SELECTOR_READ_MASK | SVM_SELECTOR_P_MASK |
		SVM_SELECTOR_S_MASK | SVM_SELECTOR_CODE_MASK;
	save->cs.limit = 0xffff;

	save->gdtr.limit = 0xffff;
	save->idtr.limit = 0xffff;

	init_sys_seg(&save->ldtr, SEG_TYPE_LDT);
	init_sys_seg(&save->tr, SEG_TYPE_BUSY_TSS16);

	svm_set_cr4(vcpu, 0);
	svm_set_efer(vcpu, 0);
	save->dr6 = 0xffff0ff0;
	kvm_set_rflags(vcpu, X86_EFLAGS_FIXED);
	save->rip = 0x0000fff0;
	vcpu->arch.regs[VCPU_REGS_RIP] = save->rip;

	/*
	 * svm_set_cr0() sets PG and WP and clears NW and CD on save->cr0.
	 * It also updates the guest-visible cr0 value.
	 */
	svm_set_cr0(vcpu, X86_CR0_NW | X86_CR0_CD | X86_CR0_ET);
	kvm_mmu_reset_context(vcpu);

	save->cr4 = X86_CR4_PAE;
	/* rdx = ?? */

	if (npt_enabled) {
		/* Setup VMCB for Nested Paging */
		control->nested_ctl |= SVM_NESTED_CTL_NP_ENABLE;
		svm_clr_intercept(svm, INTERCEPT_INVLPG);
		clr_exception_intercept(svm, PF_VECTOR);
		svm_clr_intercept(svm, INTERCEPT_CR3_READ);
		svm_clr_intercept(svm, INTERCEPT_CR3_WRITE);
		save->g_pat = vcpu->arch.pat;
		save->cr3 = 0;
		save->cr4 = 0;
	}
	svm->current_vmcb->asid_generation = 0;
	svm->asid = 0;

	svm->nested.vmcb12_gpa = INVALID_GPA;
	svm->nested.last_vmcb12_gpa = INVALID_GPA;
	vcpu->arch.hflags = 0;

	if (!kvm_pause_in_guest(vcpu->kvm)) {
		control->pause_filter_count = pause_filter_count;
		if (pause_filter_thresh)
			control->pause_filter_thresh = pause_filter_thresh;
		svm_set_intercept(svm, INTERCEPT_PAUSE);
	} else {
		svm_clr_intercept(svm, INTERCEPT_PAUSE);
	}

	svm_recalc_instruction_intercepts(vcpu, svm);

	/*
	 * If the host supports V_SPEC_CTRL then disable the interception
	 * of MSR_IA32_SPEC_CTRL.
	 */
	if (boot_cpu_has(X86_FEATURE_V_SPEC_CTRL))
		set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1);

	if (kvm_vcpu_apicv_active(vcpu))
		avic_init_vmcb(svm);

	if (vgif) {
		svm_clr_intercept(svm, INTERCEPT_STGI);
		svm_clr_intercept(svm, INTERCEPT_CLGI);
		svm->vmcb->control.int_ctl |= V_GIF_ENABLE_MASK;
	}

	if (sev_guest(vcpu->kvm)) {
		svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
		clr_exception_intercept(svm, UD_VECTOR);

		if (sev_es_guest(vcpu->kvm)) {
			/* Perform SEV-ES specific VMCB updates */
			sev_es_init_vmcb(svm);
		}
	}

	svm_hv_init_vmcb(svm->vmcb);

	vmcb_mark_all_dirty(svm->vmcb);

	enable_gif(svm);

}


OGRFeature *OGRKMLLayer::GetNextFeature()
{
#ifndef HAVE_EXPAT
    return nullptr;
#else
    /* -------------------------------------------------------------------- */
    /*      Loop till we find a feature matching our criteria.              */
    /* -------------------------------------------------------------------- */
    KML *poKMLFile = poDS_->GetKMLFile();
    if( poKMLFile == nullptr )
        return nullptr;

    poKMLFile->selectLayer(nLayerNumber_);

    while( true )
    {
        Feature *poFeatureKML =
            poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount);

        if( poFeatureKML == nullptr )
            return nullptr;

        OGRFeature *poFeature = new OGRFeature( poFeatureDefn_ );

        if( poFeatureKML->poGeom )
        {
            poFeature->SetGeometryDirectly(poFeatureKML->poGeom);
            poFeatureKML->poGeom = nullptr;
        }

        // Add fields.
        poFeature->SetField( poFeatureDefn_->GetFieldIndex("Name"),
                             poFeatureKML->sName.c_str() );
        poFeature->SetField( poFeatureDefn_->GetFieldIndex("Description"),
                             poFeatureKML->sDescription.c_str() );
        poFeature->SetFID( iNextKMLId_ - 1 );

        // Clean up.
        delete poFeatureKML;

        if( poFeature->GetGeometryRef() != nullptr && poSRS_ != nullptr )
        {
            poFeature->GetGeometryRef()->assignSpatialReference( poSRS_ );
        }

        // Check spatial/attribute filters.
        if( (m_poFilterGeom == nullptr ||
             FilterGeometry( poFeature->GetGeometryRef() ) ) &&
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate( poFeature )) )
        {
            return poFeature;
        }

        delete poFeature;
    }

#endif /* HAVE_EXPAT */
}


GIntBig OGRKMLLayer::GetFeatureCount( int /* bForce */ ) { return 0; }


action_set_classify(const struct ofpact *a)
{
    switch (a->type) {
#define SLOT(OFPACT) case OFPACT: return ACTION_SLOT_##OFPACT;
        ACTION_SET_ORDER
#undef SLOT

#define FINAL(OFPACT) case OFPACT: return ACTION_SLOT_##OFPACT;
        ACTION_SET_FINAL_PRIORITY
#undef FINAL

    case OFPACT_SET_FIELD:
    case OFPACT_REG_MOVE:
    case OFPACT_SET_ETH_DST:
    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_SET_IPV4_DST:
    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_L4_DST_PORT:
    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_SET_QUEUE:
    case OFPACT_SET_TUNNEL:
    case OFPACT_SET_VLAN_PCP:
    case OFPACT_SET_VLAN_VID:
        return ACTION_SLOT_SET_OR_MOVE;

    case OFPACT_BUNDLE:
    case OFPACT_CLEAR_ACTIONS:
    case OFPACT_CLONE:
    case OFPACT_NAT:
    case OFPACT_CONTROLLER:
    case OFPACT_ENQUEUE:
    case OFPACT_EXIT:
    case OFPACT_UNROLL_XLATE:
    case OFPACT_FIN_TIMEOUT:
    case OFPACT_GOTO_TABLE:
    case OFPACT_LEARN:
    case OFPACT_CONJUNCTION:
    case OFPACT_METER:
    case OFPACT_MULTIPATH:
    case OFPACT_NOTE:
    case OFPACT_OUTPUT_REG:
    case OFPACT_OUTPUT_TRUNC:
    case OFPACT_POP_QUEUE:
    case OFPACT_SAMPLE:
    case OFPACT_STACK_POP:
    case OFPACT_STACK_PUSH:
    case OFPACT_WRITE_ACTIONS:
    case OFPACT_WRITE_METADATA:
    case OFPACT_DEBUG_RECIRC:
    case OFPACT_DEBUG_SLOW:
        return ACTION_SLOT_INVALID;

    default:
        OVS_NOT_REACHED();
    }
}


parse_RESUBMIT(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_resubmit *resubmit;
    char *in_port_s, *table_s, *ct_s;

    resubmit = ofpact_put_RESUBMIT(pp->ofpacts);

    in_port_s = strsep(&arg, ",");
    if (in_port_s && in_port_s[0]) {
        if (!ofputil_port_from_string(in_port_s, pp->port_map,
                                      &resubmit->in_port)) {
            return xasprintf("%s: resubmit to unknown port", in_port_s);
        }
    } else {
        resubmit->in_port = OFPP_IN_PORT;
    }

    table_s = strsep(&arg, ",");
    if (table_s && table_s[0]) {
        if (!ofputil_table_from_string(table_s, pp->table_map,
                                       &resubmit->table_id)) {
            return xasprintf("%s: resubmit to unknown table", table_s);
        }
    } else {
        resubmit->table_id = 255;
    }

    ct_s = strsep(&arg, ",");
    if (ct_s && ct_s[0]) {
        if (strcmp(ct_s, "ct")) {
            return xasprintf("%s: unknown parameter", ct_s);
        }
        resubmit->with_ct_orig = true;
    } else {
        resubmit->with_ct_orig = false;
    }

    if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
        return xstrdup("at least one \"in_port\" or \"table\" must be "
                       "specified  on resubmit");
    }
    return NULL;
}


parse_DEC_TTL(char *arg, const struct ofpact_parse_params *pp)
{
    if (*arg == '\0') {
        parse_noargs_dec_ttl(pp);
    } else {
        struct ofpact_cnt_ids *ids;
        char *cntr;

        ids = ofpact_put_DEC_TTL(pp->ofpacts);
        ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS;
        for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
             cntr = strtok_r(NULL, ", ", &arg)) {
            uint16_t id = atoi(cntr);

            ofpbuf_put(pp->ofpacts, &id, sizeof id);
            ids = pp->ofpacts->header;
            ids->n_controllers++;
        }
        if (!ids->n_controllers) {
            return xstrdup("dec_ttl_cnt_ids: expected at least one controller "
                           "id.");
        }
        ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
    }
    return NULL;
}


gboolean textview_search_string(TextView *textview, const gchar *str,
				gboolean case_sens)
{
	GtkTextView *text = GTK_TEXT_VIEW(textview->text);

	return gtkut_text_view_search_string(text, str, case_sens);
}


void textview_init(TextView *textview)
{
	if (!hand_cursor)
		hand_cursor = gdk_cursor_new(GDK_HAND2);
	if (!text_cursor)
		text_cursor = gdk_cursor_new(GDK_XTERM);
	if (!watch_cursor)
		watch_cursor = gdk_cursor_new(GDK_WATCH);

	textview_reflect_prefs(textview);
	textview_set_font(textview, NULL);
	textview_create_tags(GTK_TEXT_VIEW(textview->text), textview);
}


static void textview_show_tags(TextView *textview)
{
	MsgInfo *msginfo = textview->messageview->msginfo;
	GtkTextView *text = GTK_TEXT_VIEW(textview->text);
	GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
	GtkTextIter iter;
	ClickableText *uri;
	GSList *cur, *orig;
	gboolean found_tag = FALSE;

	if (!msginfo->tags)
		return;

	cur = orig = g_slist_sort(g_slist_copy(msginfo->tags), textview_tag_cmp_list);

	for (; cur; cur = cur->next) {
		if (tags_get_tag(GPOINTER_TO_INT(cur->data)) != NULL) {
			found_tag = TRUE;
			break;
		}
	}
	if (!found_tag) {
		g_slist_free(orig);
		return;
	}

	gtk_text_buffer_get_end_iter (buffer, &iter);
	gtk_text_buffer_insert_with_tags_by_name(buffer,
		&iter, _("Tags: "), -1,
		"header_title", "header", "tags", NULL);

	for (cur = orig; cur; cur = cur->next) {
		const gchar *cur_tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
		if (!cur_tag)
			continue;
		uri = g_new0(ClickableText, 1);
		uri->uri = g_strdup("");
		uri->start = gtk_text_iter_get_offset(&iter);
		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
			cur_tag, -1,
			"link", "header", "tags", NULL);
		uri->end = gtk_text_iter_get_offset(&iter);
		uri->filename = g_strdup_printf("sc://search_tags:%s", cur_tag);
		uri->data = NULL;
		textview->uri_list =
			g_slist_prepend(textview->uri_list, uri);
		if (cur->next && tags_get_tag(GPOINTER_TO_INT(cur->next->data)))
			gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, ", ", 2,
				"header", "tags", NULL);
		else
			gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, " ", 1,
				"header", "tags", NULL);
	}
	g_slist_free(orig);

	gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, "\n", 1,
		"header", "tags", NULL);
}


static void textview_make_clickable_parts_later(TextView *textview,
					  gint start, gint end)
{
	GtkTextView *text = GTK_TEXT_VIEW(textview->text);
	GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
	GtkTextIter start_iter, end_iter;
	gchar *mybuf;
	gint offset = 0;
	/* parse table - in order of priority */
	struct table {
		const gchar *needle; /* token */

		/* token search function */
		gchar    *(*search)	(const gchar *haystack,
					 const gchar *needle);
		/* part parsing function */
		gboolean  (*parse)	(const gchar *start,
					 const gchar *scanpos,
					 const gchar **bp_,
					 const gchar **ep_,
					 gboolean hdr);
		/* part to URI function */
		gchar    *(*build_uri)	(const gchar *bp,
					 const gchar *ep);
	};

	static struct table parser[] = {
		{"http://",  strcasestr, get_uri_part,   make_uri_string},
		{"https://", strcasestr, get_uri_part,   make_uri_string},
		{"ftp://",   strcasestr, get_uri_part,   make_uri_string},
		{"sftp://",  strcasestr, get_uri_part,   make_uri_string},
		{"www.",     strcasestr, get_uri_part,   make_http_string},
		{"mailto:",  strcasestr, get_uri_part,   make_uri_string},
		{"@",        strcasestr, get_email_part, make_email_string}
	};
	const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];

	gint  n;
	const gchar *walk, *bp, *ep;

	struct txtpos {
		const gchar	*bp, *ep;	/* text position */
		gint		 pti;		/* index in parse table */
		struct txtpos	*next;		/* next */
	} head = {NULL, NULL, 0,  NULL}, *last = &head;

	gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
	gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
	mybuf = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
	offset = gtk_text_iter_get_offset(&start_iter);

	/* parse for clickable parts, and build a list of begin and end positions  */
	for (walk = mybuf;;) {
		gint last_index = PARSE_ELEMS;
		gchar *scanpos = NULL;

		/* FIXME: this looks phony. scanning for anything in the parse table */
		for (n = 0; n < PARSE_ELEMS; n++) {
			gchar *tmp;

			tmp = parser[n].search(walk, parser[n].needle);
			if (tmp) {
				if (scanpos == NULL || tmp < scanpos) {
					scanpos = tmp;
					last_index = n;
				}
			}
		}

		if (scanpos) {
			/* check if URI can be parsed */
			if (parser[last_index].parse(walk, scanpos, &bp, &ep, FALSE)
			    && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
					ADD_TXT_POS_LATER(bp, ep, last_index);
					walk = ep;
			} else
				walk = scanpos +
					strlen(parser[last_index].needle);
		} else
			break;
	}

	/* colorize this line */
	if (head.next) {
		/* insert URIs */
		for (last = head.next; last != NULL; last = last->next) {
			ClickableText *uri;
			gint start_offset, end_offset;
			gchar *tmp_str;
			gchar old_char;
			uri = g_new0(ClickableText, 1);
			uri->uri = parser[last->pti].build_uri(last->bp,
							       last->ep);

			tmp_str = mybuf;
			old_char = tmp_str[last->ep - mybuf];
			tmp_str[last->ep - mybuf] = '\0';
			end_offset = g_utf8_strlen(tmp_str, -1);
			tmp_str[last->ep - mybuf] = old_char;

			old_char = tmp_str[last->bp - mybuf];
			tmp_str[last->bp - mybuf] = '\0';
			start_offset = g_utf8_strlen(tmp_str, -1);
			tmp_str[last->bp - mybuf] = old_char;

			gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start_offset + offset);
			gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end_offset + offset);

			uri->start = gtk_text_iter_get_offset(&start_iter);

			gtk_text_buffer_apply_tag_by_name(buffer, "link", &start_iter, &end_iter);

			uri->end = gtk_text_iter_get_offset(&end_iter);
			uri->filename = NULL;
			textview->uri_list =
				g_slist_prepend(textview->uri_list, uri);
		}
	}

	g_free(mybuf);
}


void r_bin_le_free(r_bin_le_obj_t *bin) {
	r_return_if_fail (bin);
	free (bin->header);
	free (bin->objtbl);
	free (bin->filename);
	free (bin);
}


static void rb_inc_iter(struct ring_buffer_iter *iter)
{
	struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;

	/*
	 * The iterator could be on the reader page (it starts there).
	 * But the head could have moved, since the reader was
	 * found. Check for this case and assign the iterator
	 * to the head page instead of next.
	 */
	if (iter->head_page == cpu_buffer->reader_page)
		iter->head_page = rb_set_head_page(cpu_buffer);
	else
		rb_inc_page(&iter->head_page);

	iter->page_stamp = iter->read_stamp = iter->head_page->page->time_stamp;
	iter->head = 0;
	iter->next_event = 0;
}


u64 ring_buffer_time_stamp(struct trace_buffer *buffer)
{
	u64 time;

	preempt_disable_notrace();
	time = rb_time_stamp(buffer);
	preempt_enable_notrace();

	return time;
}


rb_buffer_peek(struct ring_buffer_per_cpu *cpu_buffer, u64 *ts,
	       unsigned long *lost_events)
{
	struct ring_buffer_event *event;
	struct buffer_page *reader;
	int nr_loops = 0;

	if (ts)
		*ts = 0;
 again:
	/*
	 * We repeat when a time extend is encountered.
	 * Since the time extend is always attached to a data event,
	 * we should never loop more than once.
	 * (We never hit the following condition more than twice).
	 */
	if (RB_WARN_ON(cpu_buffer, ++nr_loops > 2))
		return NULL;

	reader = rb_get_reader_page(cpu_buffer);
	if (!reader)
		return NULL;

	event = rb_reader_event(cpu_buffer);

	switch (event->type_len) {
	case RINGBUF_TYPE_PADDING:
		if (rb_null_event(event))
			RB_WARN_ON(cpu_buffer, 1);
		/*
		 * Because the writer could be discarding every
		 * event it creates (which would probably be bad)
		 * if we were to go back to "again" then we may never
		 * catch up, and will trigger the warn on, or lock
		 * the box. Return the padding, and we will release
		 * the current locks, and try again.
		 */
		return event;

	case RINGBUF_TYPE_TIME_EXTEND:
		/* Internal data, OK to advance */
		rb_advance_reader(cpu_buffer);
		goto again;

	case RINGBUF_TYPE_TIME_STAMP:
		if (ts) {
			*ts = rb_event_time_stamp(event);
			ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
							 cpu_buffer->cpu, ts);
		}
		/* Internal data, OK to advance */
		rb_advance_reader(cpu_buffer);
		goto again;

	case RINGBUF_TYPE_DATA:
		if (ts && !(*ts)) {
			*ts = cpu_buffer->read_stamp + event->time_delta;
			ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
							 cpu_buffer->cpu, ts);
		}
		if (lost_events)
			*lost_events = rb_lost_events(cpu_buffer);
		return event;

	default:
		RB_WARN_ON(cpu_buffer, 1);
	}

	return NULL;
}


static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer,
			 struct rb_event_info *info,
			 unsigned long tail)
{
	struct ring_buffer_event *event;
	struct buffer_data_page *bpage;
	u64 ts, delta;
	bool full = false;
	int e;

	bpage = info->tail_page->page;

	if (tail == CHECK_FULL_PAGE) {
		full = true;
		tail = local_read(&bpage->commit);
	} else if (info->add_timestamp &
		   (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)) {
		/* Ignore events with absolute time stamps */
		return;
	}

	/*
	 * Do not check the first event (skip possible extends too).
	 * Also do not check if previous events have not been committed.
	 */
	if (tail <= 8 || tail > local_read(&bpage->commit))
		return;

	/*
	 * If this interrupted another event,
	 */
	if (atomic_inc_return(this_cpu_ptr(&checking)) != 1)
		goto out;

	ts = bpage->time_stamp;

	for (e = 0; e < tail; e += rb_event_length(event)) {

		event = (struct ring_buffer_event *)(bpage->data + e);

		switch (event->type_len) {

		case RINGBUF_TYPE_TIME_EXTEND:
			delta = rb_event_time_stamp(event);
			ts += delta;
			break;

		case RINGBUF_TYPE_TIME_STAMP:
			delta = rb_event_time_stamp(event);
			ts = delta;
			break;

		case RINGBUF_TYPE_PADDING:
			if (event->time_delta == 1)
				break;
			fallthrough;
		case RINGBUF_TYPE_DATA:
			ts += event->time_delta;
			break;

		default:
			RB_WARN_ON(cpu_buffer, 1);
		}
	}
	if ((full && ts > info->ts) ||
	    (!full && ts + info->delta != info->ts)) {
		/* If another report is happening, ignore this one */
		if (atomic_inc_return(&ts_dump) != 1) {
			atomic_dec(&ts_dump);
			goto out;
		}
		atomic_inc(&cpu_buffer->record_disabled);
		/* There's some cases in boot up that this can happen */
		WARN_ON_ONCE(system_state != SYSTEM_BOOTING);
		pr_warn("[CPU: %d]TIME DOES NOT MATCH expected:%lld actual:%lld delta:%lld before:%lld after:%lld%s\n",
			cpu_buffer->cpu,
			ts + info->delta, info->ts, info->delta,
			info->before, info->after,
			full ? " (full)" : "");
		dump_buffer_page(bpage, info, tail);
		atomic_dec(&ts_dump);
		/* Do not re-enable checking */
		return;
	}
out:
	atomic_dec(this_cpu_ptr(&checking));
}


static u64 rb_time_delta(struct ring_buffer_event *event)
{
	switch (event->type_len) {
	case RINGBUF_TYPE_PADDING:
		return 0;

	case RINGBUF_TYPE_TIME_EXTEND:
		return rb_event_time_stamp(event);

	case RINGBUF_TYPE_TIME_STAMP:
		return 0;

	case RINGBUF_TYPE_DATA:
		return event->time_delta;
	default:
		return 0;
	}
}


rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
		  struct ring_buffer_event *event)
{
	unsigned long new_index, old_index;
	struct buffer_page *bpage;
	unsigned long index;
	unsigned long addr;
	u64 write_stamp;
	u64 delta;

	new_index = rb_event_index(event);
	old_index = new_index + rb_event_ts_length(event);
	addr = (unsigned long)event;
	addr &= PAGE_MASK;

	bpage = READ_ONCE(cpu_buffer->tail_page);

	delta = rb_time_delta(event);

	if (!rb_time_read(&cpu_buffer->write_stamp, &write_stamp))
		return 0;

	/* Make sure the write stamp is read before testing the location */
	barrier();

	if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) {
		unsigned long write_mask =
			local_read(&bpage->write) & ~RB_WRITE_MASK;
		unsigned long event_length = rb_event_length(event);

		/* Something came in, can't discard */
		if (!rb_time_cmpxchg(&cpu_buffer->write_stamp,
				       write_stamp, write_stamp - delta))
			return 0;

		/*
		 * It's possible that the event time delta is zero
		 * (has the same time stamp as the previous event)
		 * in which case write_stamp and before_stamp could
		 * be the same. In such a case, force before_stamp
		 * to be different than write_stamp. It doesn't
		 * matter what it is, as long as its different.
		 */
		if (!delta)
			rb_time_set(&cpu_buffer->before_stamp, 0);

		/*
		 * If an event were to come in now, it would see that the
		 * write_stamp and the before_stamp are different, and assume
		 * that this event just added itself before updating
		 * the write stamp. The interrupting event will fix the
		 * write stamp for us, and use the before stamp as its delta.
		 */

		/*
		 * This is on the tail page. It is possible that
		 * a write could come in and move the tail page
		 * and write to the next page. That is fine
		 * because we just shorten what is on this page.
		 */
		old_index += write_mask;
		new_index += write_mask;
		index = local_cmpxchg(&bpage->write, old_index, new_index);
		if (index == old_index) {
			/* update counters */
			local_sub(event_length, &cpu_buffer->entries_bytes);
			return 1;
		}
	}

	/* could not discard */
	return 0;
}


void avahi_s_domain_browser_start(AvahiSDomainBrowser *b) {
    assert(b);

    if(b->record_browser)
        avahi_s_record_browser_start_query(b->record_browser);
}


void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
    assert(r);

    AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);

    if (r->record_browser)
        avahi_s_record_browser_free(r->record_browser);

    if (r->time_event)
        avahi_time_event_free(r->time_event);

    if (r->ptr_record)
        avahi_record_unref(r->ptr_record);

    if (r->key)
        avahi_key_unref(r->key);

    avahi_free(r);
}


static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
    AvahiSServiceResolver *r = userdata;

    assert(e);
    assert(r);

    avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
    finish(r, AVAHI_RESOLVER_FAILURE);
}


static void start_timeout(AvahiSAddressResolver *r) {
    struct timeval tv;
    assert(r);

    if (r->time_event)
        return;

    avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
    r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
}


static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) {
    assert(l);
    assert(l->ref >= 1);

    l->ref++;
    return l;
}


AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(
    AvahiServer *server,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    const char *domain,
    AvahiDNSServerType type,
    AvahiProtocol aprotocol,
    AvahiLookupFlags flags,
    AvahiSDNSServerBrowserCallback callback,
    void* userdata) {
        AvahiSDNSServerBrowser* b;

        b = avahi_s_dns_server_browser_prepare(server, interface, protocol, domain, type, aprotocol, flags, callback, userdata);
        if (!b)
            return NULL;

        avahi_s_dns_server_browser_start(b);

        return b;
}


static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
    uint8_t status, uint8_t ep, void *free_on_destroy)
{
    struct buf_packet *bufp;

    if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
        dev->endpoint[EP2I(ep)].bufpq_size >
            2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
        DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
    }
    /* Since we're interupting the stream anyways, drop enough packets to get
       back to our target buffer size */
    if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
        if (dev->endpoint[EP2I(ep)].bufpq_size >
                dev->endpoint[EP2I(ep)].bufpq_target_size) {
            free(free_on_destroy);
            return -1;
        }
        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
    }

    bufp = g_new(struct buf_packet, 1);
    bufp->data   = data;
    bufp->len    = len;
    bufp->offset = 0;
    bufp->status = status;
    bufp->free_on_destroy = free_on_destroy;
    QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
    dev->endpoint[EP2I(ep)].bufpq_size++;
    return 0;
}


write_out_binary_header (dev_t rdev,
			 struct cpio_file_stat *file_hdr, int out_des)
{
  struct old_cpio_header short_hdr;

  short_hdr.c_magic = 070707;
  short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min);

  if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0)
    error (0, 0, _("%s: truncating inode number"), file_hdr->c_name);

  short_hdr.c_ino = file_hdr->c_ino & 0xFFFF;
  if (short_hdr.c_ino != file_hdr->c_ino)
    field_width_warning (file_hdr->c_name, _("inode number"));

  short_hdr.c_mode = file_hdr->c_mode & 0xFFFF;
  if (short_hdr.c_mode != file_hdr->c_mode)
    field_width_warning (file_hdr->c_name, _("file mode"));

  short_hdr.c_uid = file_hdr->c_uid & 0xFFFF;
  if (short_hdr.c_uid != file_hdr->c_uid)
    field_width_warning (file_hdr->c_name, _("uid"));

  short_hdr.c_gid = file_hdr->c_gid & 0xFFFF;
  if (short_hdr.c_gid != file_hdr->c_gid)
    field_width_warning (file_hdr->c_name, _("gid"));

  short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF;
  if (short_hdr.c_nlink != file_hdr->c_nlink)
    field_width_warning (file_hdr->c_name, _("number of links"));

  short_hdr.c_rdev = rdev;
  short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16;
  short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF;

  short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF;
  if (short_hdr.c_namesize != file_hdr->c_namesize)
    {
      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
      error (0, 0, _("%s: value %s %s out of allowed range 0..%u"),
	     file_hdr->c_name, _("name size"),
	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFu);
      return 1;
    }

  short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16;
  short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF;

  if (((off_t)short_hdr.c_filesizes[0] << 16) + short_hdr.c_filesizes[1]
       != file_hdr->c_filesize)
    {
      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
      error (0, 0, _("%s: value %s %s out of allowed range 0..%lu"),
	     file_hdr->c_name, _("file size"),
	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFFFFFlu);
      return 1;
    }

  /* Output the file header.  */
  tape_buffered_write ((char *) &short_hdr, out_des, 26);

  /* Write file name to output.  */
  tape_buffered_write (file_hdr->c_name, out_des, file_hdr->c_namesize);

  tape_pad_output (out_des, file_hdr->c_namesize + 26);
  return 0;
}


write_out_new_ascii_header (const char *magic_string,
			    struct cpio_file_stat *file_hdr, int out_des)
{
  char ascii_header[110];
  char *p;

  p = stpcpy (ascii_header, magic_string);
  to_ascii_or_warn (p, file_hdr->c_ino, 8, LG_16,
		    file_hdr->c_name, _("inode number"));
  p += 8;
  to_ascii_or_warn (p, file_hdr->c_mode, 8, LG_16, file_hdr->c_name,
		    _("file mode"));
  p += 8;
  to_ascii_or_warn (p, file_hdr->c_uid, 8, LG_16, file_hdr->c_name,
		    _("uid"));
  p += 8;
  to_ascii_or_warn (p, file_hdr->c_gid, 8, LG_16, file_hdr->c_name,
		    _("gid"));
  p += 8;
  to_ascii_or_warn (p, file_hdr->c_nlink, 8, LG_16, file_hdr->c_name,
		    _("number of links"));
  p += 8;
  to_ascii_or_warn (p, file_hdr->c_mtime, 8, LG_16, file_hdr->c_name,
		    _("modification time"));
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_filesize, 8, LG_16, file_hdr->c_name,
			 _("file size")))
    return 1;
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_dev_maj, 8, LG_16, file_hdr->c_name,
			 _("device major number")))
    return 1;
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_dev_min, 8, LG_16, file_hdr->c_name,
			 _("device minor number")))
    return 1;
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_rdev_maj, 8, LG_16, file_hdr->c_name,
			 _("rdev major")))
    return 1;
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_rdev_min, 8, LG_16, file_hdr->c_name,
			 _("rdev minor")))
    return 1;
  p += 8;
  if (to_ascii_or_error (p, file_hdr->c_namesize, 8, LG_16, file_hdr->c_name,
			 _("name size")))
    return 1;
  p += 8;
  to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16, false);

  tape_buffered_write (ascii_header, out_des, sizeof ascii_header);

  /* Write file name to output.  */
  tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize);
  tape_pad_output (out_des, file_hdr->c_namesize + sizeof ascii_header);
  return 0;
}


writeout_final_defers (int out_des)
{
  struct deferment *d;
  int other_count;
  while (deferouts != NULL)
    {
      d = deferouts;
      other_count = count_defered_links_to_dev_ino (&d->header);
      if (other_count == 1)
	{
	  writeout_defered_file (&d->header, out_des);
	}
      else
	{
	  struct cpio_file_stat file_hdr;
	  file_hdr = d->header;
	  file_hdr.c_filesize = 0;
	  write_out_header (&file_hdr, out_des);
	}
      deferouts = deferouts->next;
    }
}


fchown_or_chown (int fd, const char *name, uid_t uid, uid_t gid)
{
  if (HAVE_FCHOWN && fd != -1)
    return fchown (fd, uid, gid);
  else
    return chown (name, uid, gid);
}


tape_buffered_peek (char *peek_buf, int in_des, int num_bytes)
{
  long tmp_input_size;
  long got_bytes;
  char *append_buf;

#ifdef BROKEN_LONG_TAPE_DRIVER
  /* Some tape drivers seem to have a signed internal seek pointer and
     they lose if it overflows and becomes negative (e.g. when writing
     tapes > 4Gb).  Doing an lseek (des, 0, SEEK_SET) seems to reset the
     seek pointer and prevent it from overflowing.  */
  if (input_is_special
      && ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
    {
      lseek(in_des, 0L, SEEK_SET);
      input_bytes_before_lseek = 0;
    }
#endif

  while (input_size < num_bytes)
    {
      append_buf = in_buff + input_size;
      if ( (append_buf - input_buffer) >= input_buffer_size)
	{
	  /* We can keep up to 2 "blocks" (either the physical block size
	     or 512 bytes(the size of a tar record), which ever is
	     larger) in the input buffer when we are peeking.  We
	     assume that our caller will never be interested in peeking
	     ahead at more than 512 bytes, so we know that by the time
	     we need a 3rd "block" in the buffer we can throw away the
	     first block to make room.  */
	  int half;
	  half = input_buffer_size / 2;
	  memmove (input_buffer, input_buffer + half, half);
	  in_buff = in_buff - half;
	  append_buf = append_buf - half;
	}
      tmp_input_size = rmtread (in_des, append_buf, io_block_size);
      if (tmp_input_size == 0)
	{
	  if (input_is_special)
	    {
	      get_next_reel (in_des);
	      tmp_input_size = rmtread (in_des, append_buf, io_block_size);
	    }
	  else
	    break;
	}
      if (tmp_input_size < 0)
	error (PAXEXIT_FAILURE, errno, _("read error"));
      input_bytes += tmp_input_size;
      input_size += tmp_input_size;
    }
  if (num_bytes <= input_size)
    got_bytes = num_bytes;
  else
    got_bytes = input_size;
  memcpy (peek_buf, in_buff, (unsigned) got_bytes);
  return got_bytes;
}


tape_pad_output (int out_file_des, int offset)
{
  size_t pad;

  if (archive_format == arf_newascii || archive_format == arf_crcascii)
    pad = (4 - (offset % 4)) % 4;
  else if (archive_format == arf_tar || archive_format == arf_ustar)
    pad = (512 - (offset % 512)) % 512;
  else if (archive_format != arf_oldascii && archive_format != arf_hpoldascii)
    pad = (2 - (offset % 2)) % 2;
  else
    pad = 0;

  if (pad != 0)
    write_nuls_to_file (pad, out_file_des, tape_buffered_write);
}


get_inode_and_dev (struct cpio_file_stat *hdr, struct stat *st)
{
  if (renumber_inodes_option)
    {
      if (st->st_nlink > 1)
	{
	  struct inode_val *ival = find_inode_val (st->st_ino,
						   major (st->st_dev),
						   minor (st->st_dev));
	  if (!ival)
	    ival = add_inode (st->st_ino, NULL,
			      major (st->st_dev), minor (st->st_dev));
	  hdr->c_ino = ival->trans_inode;
	}
      else
	hdr->c_ino = next_inode++;
    }
  else
    hdr->c_ino = st->st_ino;
  if (ignore_devno_option)
    {
      hdr->c_dev_maj = 0;
      hdr->c_dev_min = 0;
    }
  else
    {
      hdr->c_dev_maj = major (st->st_dev);
      hdr->c_dev_min = minor (st->st_dev);
    }
}


disk_fill_input_buffer (int in_des, off_t num_bytes)
{
  in_buff = input_buffer;
  num_bytes = (num_bytes < DISK_IO_BLOCK_SIZE) ? num_bytes : DISK_IO_BLOCK_SIZE;
  input_size = read (in_des, input_buffer, num_bytes);
  if (input_size == SAFE_READ_ERROR)
    {
      input_size = 0;
      return (-1);
    }
  else if (input_size == 0)
    return (1);
  input_bytes += input_size;
  return (0);
}


set_new_media_message (char *message)
{
  char *p;
  int prev_was_percent;

  p = message;
  prev_was_percent = 0;
  while (*p != '\0')
    {
      if (*p == 'd' && prev_was_percent)
	break;
      prev_was_percent = (*p == '%');
      ++p;
    }
  if (*p == '\0')
    {
      new_media_message = xstrdup (message);
    }
  else
    {
      int length = p - message - 1;

      new_media_message_with_number = xmalloc (length + 1);
      strncpy (new_media_message_with_number, message, length);
      new_media_message_with_number[length] = '\0';
      length = strlen (p + 1);
      new_media_message_after_number = xmalloc (length + 1);
      strcpy (new_media_message_after_number, p + 1);
    }
}


to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase, bool nul)
{
  static char codetab[] = "0123456789ABCDEF";

  if (nul)
    where[--digits] = 0;
  while (digits > 0)
    {
      where[--digits] = codetab[(v & ((1 << logbase) - 1))];
      v >>= logbase;
    }

  return v != 0;
}


ds_endswith (dynamic_string *s, int c)
{
  return (s->ds_idx > 0 && s->ds_string[s->ds_idx - 1] == c);
}


static void usbredirparser_call_type_func(struct usbredirparser *parser_pub,
    bool *data_ownership_transferred)
{
    struct usbredirparser_priv *parser =
        (struct usbredirparser_priv *)parser_pub;
    uint64_t id;

    if (usbredirparser_using_32bits_ids(parser_pub))
        id = parser->header_32bit_id.id;
    else
        id = parser->header.id;

    switch (parser->header.type) {
    case usb_redir_hello:
        usbredirparser_handle_hello(parser_pub,
            (struct usb_redir_hello_header *)parser->type_header,
            parser->data, parser->data_len);
        break;
    case usb_redir_device_connect:
        parser->callb.device_connect_func(parser->callb.priv,
            (struct usb_redir_device_connect_header *)parser->type_header);
        break;
    case usb_redir_device_disconnect:
        parser->callb.device_disconnect_func(parser->callb.priv);
        if (usbredirparser_peer_has_cap(parser_pub,
                                        usb_redir_cap_device_disconnect_ack))
            usbredirparser_queue(parser_pub, usb_redir_device_disconnect_ack,
                                 0, NULL, NULL, 0);
        break;
    case usb_redir_reset:
        parser->callb.reset_func(parser->callb.priv);
        break;
    case usb_redir_interface_info:
        parser->callb.interface_info_func(parser->callb.priv,
            (struct usb_redir_interface_info_header *)parser->type_header);
        break;
    case usb_redir_ep_info:
        parser->callb.ep_info_func(parser->callb.priv,
            (struct usb_redir_ep_info_header *)parser->type_header);
        break;
    case usb_redir_set_configuration:
        parser->callb.set_configuration_func(parser->callb.priv, id,
            (struct usb_redir_set_configuration_header *)parser->type_header);
        break;
    case usb_redir_get_configuration:
        parser->callb.get_configuration_func(parser->callb.priv, id);
        break;
    case usb_redir_configuration_status:
        parser->callb.configuration_status_func(parser->callb.priv, id,
          (struct usb_redir_configuration_status_header *)parser->type_header);
        break;
    case usb_redir_set_alt_setting:
        parser->callb.set_alt_setting_func(parser->callb.priv, id,
            (struct usb_redir_set_alt_setting_header *)parser->type_header);
        break;
    case usb_redir_get_alt_setting:
        parser->callb.get_alt_setting_func(parser->callb.priv, id,
            (struct usb_redir_get_alt_setting_header *)parser->type_header);
        break;
    case usb_redir_alt_setting_status:
        parser->callb.alt_setting_status_func(parser->callb.priv, id,
            (struct usb_redir_alt_setting_status_header *)parser->type_header);
        break;
    case usb_redir_start_iso_stream:
        parser->callb.start_iso_stream_func(parser->callb.priv, id,
            (struct usb_redir_start_iso_stream_header *)parser->type_header);
        break;
    case usb_redir_stop_iso_stream:
        parser->callb.stop_iso_stream_func(parser->callb.priv, id,
            (struct usb_redir_stop_iso_stream_header *)parser->type_header);
        break;
    case usb_redir_iso_stream_status:
        parser->callb.iso_stream_status_func(parser->callb.priv, id,
            (struct usb_redir_iso_stream_status_header *)parser->type_header);
        break;
    case usb_redir_start_interrupt_receiving:
        parser->callb.start_interrupt_receiving_func(parser->callb.priv, id,
            (struct usb_redir_start_interrupt_receiving_header *)
            parser->type_header);
        break;
    case usb_redir_stop_interrupt_receiving:
        parser->callb.stop_interrupt_receiving_func(parser->callb.priv, id,
            (struct usb_redir_stop_interrupt_receiving_header *)
            parser->type_header);
        break;
    case usb_redir_interrupt_receiving_status:
        parser->callb.interrupt_receiving_status_func(parser->callb.priv, id,
            (struct usb_redir_interrupt_receiving_status_header *)
            parser->type_header);
        break;
    case usb_redir_alloc_bulk_streams:
        parser->callb.alloc_bulk_streams_func(parser->callb.priv, id,
            (struct usb_redir_alloc_bulk_streams_header *)parser->type_header);
        break;
    case usb_redir_free_bulk_streams:
        parser->callb.free_bulk_streams_func(parser->callb.priv, id,
            (struct usb_redir_free_bulk_streams_header *)parser->type_header);
        break;
    case usb_redir_bulk_streams_status:
        parser->callb.bulk_streams_status_func(parser->callb.priv, id,
          (struct usb_redir_bulk_streams_status_header *)parser->type_header);
        break;
    case usb_redir_cancel_data_packet:
        parser->callb.cancel_data_packet_func(parser->callb.priv, id);
        break;
    case usb_redir_filter_reject:
        parser->callb.filter_reject_func(parser->callb.priv);
        break;
    case usb_redir_filter_filter: {
        struct usbredirfilter_rule *rules;
        int r, count;

        r = usbredirfilter_string_to_rules((char *)parser->data, ",", "|",
                                           &rules, &count);
        if (r) {
            ERROR("error parsing filter (%d), ignoring filter message", r);
            break;
        }
        parser->callb.filter_filter_func(parser->callb.priv, rules, count);
        break;
    }
    case usb_redir_device_disconnect_ack:
        parser->callb.device_disconnect_ack_func(parser->callb.priv);
        break;
    case usb_redir_start_bulk_receiving:
        parser->callb.start_bulk_receiving_func(parser->callb.priv, id,
            (struct usb_redir_start_bulk_receiving_header *)
            parser->type_header);
        break;
    case usb_redir_stop_bulk_receiving:
        parser->callb.stop_bulk_receiving_func(parser->callb.priv, id,
            (struct usb_redir_stop_bulk_receiving_header *)
            parser->type_header);
        break;
    case usb_redir_bulk_receiving_status:
        parser->callb.bulk_receiving_status_func(parser->callb.priv, id,
            (struct usb_redir_bulk_receiving_status_header *)
            parser->type_header);
        break;
    case usb_redir_control_packet:
        *data_ownership_transferred = true;
        parser->callb.control_packet_func(parser->callb.priv, id,
            (struct usb_redir_control_packet_header *)parser->type_header,
            parser->data, parser->data_len);
        break;
    case usb_redir_bulk_packet:
        *data_ownership_transferred = true;
        parser->callb.bulk_packet_func(parser->callb.priv, id,
            (struct usb_redir_bulk_packet_header *)parser->type_header,
            parser->data, parser->data_len);
        break;
    case usb_redir_iso_packet:
        *data_ownership_transferred = true;
        parser->callb.iso_packet_func(parser->callb.priv, id,
            (struct usb_redir_iso_packet_header *)parser->type_header,
            parser->data, parser->data_len);
        break;
    case usb_redir_interrupt_packet:
        *data_ownership_transferred = true;
        parser->callb.interrupt_packet_func(parser->callb.priv, id,
            (struct usb_redir_interrupt_packet_header *)parser->type_header,
            parser->data, parser->data_len);
        break;
    case usb_redir_buffered_bulk_packet:
        *data_ownership_transferred = true;
        parser->callb.buffered_bulk_packet_func(parser->callb.priv, id,
          (struct usb_redir_buffered_bulk_packet_header *)parser->type_header,
          parser->data, parser->data_len);
        break;
    }
}


static int usbredirparser_get_type_header_len(
    struct usbredirparser *parser_pub, int32_t type, int send)
{
    struct usbredirparser_priv *parser =
        (struct usbredirparser_priv *)parser_pub;
    int command_for_host = 0;

    if (parser->flags & usbredirparser_fl_usb_host) {
        command_for_host = 1;
    }
    if (send) {
        command_for_host = !command_for_host;
    }

    switch (type) {
    case usb_redir_hello:
        return sizeof(struct usb_redir_hello_header);
    case usb_redir_device_connect:
        if (!command_for_host) {
            if (usbredirparser_have_cap(parser_pub,
                                    usb_redir_cap_connect_device_version) &&
                usbredirparser_peer_has_cap(parser_pub,
                                    usb_redir_cap_connect_device_version)) {
                return sizeof(struct usb_redir_device_connect_header);
            } else {
                return sizeof(struct usb_redir_device_connect_header_no_device_version);
            }
        } else {
            return -1;
        }
    case usb_redir_device_disconnect:
        if (!command_for_host) {
            return 0;
        } else {
            return -1;
        }
    case usb_redir_reset:
        if (command_for_host) {
            return 0; /* No packet type specific header */
        } else {
            return -1;
        }
    case usb_redir_interface_info:
        if (!command_for_host) {
            return sizeof(struct usb_redir_interface_info_header);
        } else {
            return -1;
        }
    case usb_redir_ep_info:
        if (!command_for_host) {
            if (usbredirparser_have_cap(parser_pub,
                                    usb_redir_cap_bulk_streams) &&
                usbredirparser_peer_has_cap(parser_pub,
                                    usb_redir_cap_bulk_streams)) {
                return sizeof(struct usb_redir_ep_info_header);
            } else if (usbredirparser_have_cap(parser_pub,
                                    usb_redir_cap_ep_info_max_packet_size) &&
                       usbredirparser_peer_has_cap(parser_pub,
                                    usb_redir_cap_ep_info_max_packet_size)) {
                return sizeof(struct usb_redir_ep_info_header_no_max_streams);
            } else {
                return sizeof(struct usb_redir_ep_info_header_no_max_pktsz);
            }
        } else {
            return -1;
        }
    case usb_redir_set_configuration:
        if (command_for_host) {
            return sizeof(struct usb_redir_set_configuration_header);
        } else {
            return -1; /* Should never be send to a guest */
        }
    case usb_redir_get_configuration:
        if (command_for_host) {
            return 0; /* No packet type specific header */
        } else {
            return -1;
        }
    case usb_redir_configuration_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_configuration_status_header);
        } else {
            return -1;
        }
    case usb_redir_set_alt_setting:
        if (command_for_host) {
            return sizeof(struct usb_redir_set_alt_setting_header);
        } else {
            return -1;
        }
    case usb_redir_get_alt_setting:
        if (command_for_host) {
            return sizeof(struct usb_redir_get_alt_setting_header);
        } else {
            return -1;
        }
    case usb_redir_alt_setting_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_alt_setting_status_header);
        } else {
            return -1;
        }
    case usb_redir_start_iso_stream:
        if (command_for_host) {
            return sizeof(struct usb_redir_start_iso_stream_header);
        } else {
            return -1;
        }
    case usb_redir_stop_iso_stream:
        if (command_for_host) {
            return sizeof(struct usb_redir_stop_iso_stream_header);
        } else {
            return -1;
        }
    case usb_redir_iso_stream_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_iso_stream_status_header);
        } else {
            return -1;
        }
    case usb_redir_start_interrupt_receiving:
        if (command_for_host) {
            return sizeof(struct usb_redir_start_interrupt_receiving_header);
        } else {
            return -1;
        }
    case usb_redir_stop_interrupt_receiving:
        if (command_for_host) {
            return sizeof(struct usb_redir_stop_interrupt_receiving_header);
        } else {
            return -1;
        }
    case usb_redir_interrupt_receiving_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_interrupt_receiving_status_header);
        } else {
            return -1;
        }
    case usb_redir_alloc_bulk_streams:
        if (command_for_host) {
            return sizeof(struct usb_redir_alloc_bulk_streams_header);
        } else {
            return -1;
        }
    case usb_redir_free_bulk_streams:
        if (command_for_host) {
            return sizeof(struct usb_redir_free_bulk_streams_header);
        } else {
            return -1;
        }
    case usb_redir_bulk_streams_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_bulk_streams_status_header);
        } else {
            return -1;
        }
    case usb_redir_cancel_data_packet:
        if (command_for_host) {
            return 0; /* No packet type specific header */
        } else {
            return -1;
        }
    case usb_redir_filter_reject:
        if (command_for_host) {
            return 0;
        } else {
            return -1;
        }
    case usb_redir_filter_filter:
        return 0;
    case usb_redir_device_disconnect_ack:
        if (command_for_host) {
            return 0;
        } else {
            return -1;
        }
    case usb_redir_start_bulk_receiving:
        if (command_for_host) {
            return sizeof(struct usb_redir_start_bulk_receiving_header);
        } else {
            return -1;
        }
    case usb_redir_stop_bulk_receiving:
        if (command_for_host) {
            return sizeof(struct usb_redir_stop_bulk_receiving_header);
        } else {
            return -1;
        }
    case usb_redir_bulk_receiving_status:
        if (!command_for_host) {
            return sizeof(struct usb_redir_bulk_receiving_status_header);
        } else {
            return -1;
        }
    case usb_redir_control_packet:
        return sizeof(struct usb_redir_control_packet_header);
    case usb_redir_bulk_packet:
        if (usbredirparser_have_cap(parser_pub,
                                usb_redir_cap_32bits_bulk_length) &&
            usbredirparser_peer_has_cap(parser_pub,
                                usb_redir_cap_32bits_bulk_length)) {
            return sizeof(struct usb_redir_bulk_packet_header);
        } else {
            return sizeof(struct usb_redir_bulk_packet_header_16bit_length);
        }
    case usb_redir_iso_packet:
        return sizeof(struct usb_redir_iso_packet_header);
    case usb_redir_interrupt_packet:
        return sizeof(struct usb_redir_interrupt_packet_header);
    case usb_redir_buffered_bulk_packet:
        if (!command_for_host) {
            return sizeof(struct usb_redir_buffered_bulk_packet_header);
        } else {
            return -1;
        }
    default:
        return -1;
    }
}


int usbredirparser_do_write(struct usbredirparser *parser_pub)
{
    struct usbredirparser_priv *parser =
        (struct usbredirparser_priv *)parser_pub;
    struct usbredirparser_buf* wbuf;
    int w, ret = 0;

    LOCK(parser);
    for (;;) {
        wbuf = parser->write_buf;
        if (!wbuf)
            break;

        w = wbuf->len - wbuf->pos;
        w = parser->callb.write_func(parser->callb.priv,
                                     wbuf->buf + wbuf->pos, w);
        if (w <= 0) {
            ret = w;
            break;
        }

        /* See usbredirparser_write documentation */
        if ((parser->flags & usbredirparser_fl_write_cb_owns_buffer) &&
                w != wbuf->len)
            abort();

        wbuf->pos += w;
        if (wbuf->pos == wbuf->len) {
            parser->write_buf = wbuf->next;
            if (!(parser->flags & usbredirparser_fl_write_cb_owns_buffer))
                free(wbuf->buf);
            free(wbuf);
            parser->write_buf_count--;
        }
    }
    UNLOCK(parser);
    return ret;
}


void usbredirparser_caps_set_cap(uint32_t *caps, int cap)
{
    caps[cap / 32] |= 1 << (cap % 32);
}


static void serialize_test(struct usbredirparser *parser_pub)
{
    struct usbredirparser_priv *parser =
        (struct usbredirparser_priv *)parser_pub;
    struct usbredirparser_buf *wbuf, *next_wbuf;
    uint8_t *data;
    int len;

    if (usbredirparser_serialize(parser_pub, &data, &len))
        return;

    wbuf = parser->write_buf;
    while (wbuf) {
        next_wbuf = wbuf->next;
        free(wbuf->buf);
        free(wbuf);
        wbuf = next_wbuf;
    }
    parser->write_buf = NULL;
    parser->write_buf_count = 0;

    free(parser->data);
    parser->data = NULL;

    parser->type_header_len = parser->data_len = parser->have_peer_caps = 0;

    usbredirparser_unserialize(parser_pub, data, len);
    free(data);
}


void usbredirparser_send_bulk_packet(struct usbredirparser *parser,
    uint64_t id,
    struct usb_redir_bulk_packet_header *bulk_header,
    uint8_t *data, int data_len)
{
    usbredirparser_queue(parser, usb_redir_bulk_packet, id, bulk_header,
                         data, data_len);
}


static int usbredirparser_using_32bits_ids(struct usbredirparser *parser_pub)
{
    return !usbredirparser_have_cap(parser_pub, usb_redir_cap_64bits_ids) ||
           !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_64bits_ids);
}


int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
				void *key, void *value, u64 map_flags)
{
	void *ptr;
	int ret;
	u32 ufd = *(u32 *)value;

	ptr = map->ops->map_fd_get_ptr(map, map_file, ufd);
	if (IS_ERR(ptr))
		return PTR_ERR(ptr);

	ret = htab_map_update_elem(map, key, &ptr, map_flags);
	if (ret)
		map->ops->map_fd_put_ptr(ptr);

	return ret;
}


static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
{
	struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
	struct hlist_nulls_head *head;
	struct htab_elem *l, *next_l;
	u32 hash, key_size;
	int i = 0;

	WARN_ON_ONCE(!rcu_read_lock_held());

	key_size = map->key_size;

	if (!key)
		goto find_first_elem;

	hash = htab_map_hash(key, key_size, htab->hashrnd);

	head = select_bucket(htab, hash);

	/* lookup the key */
	l = lookup_nulls_elem_raw(head, hash, key, key_size, htab->n_buckets);

	if (!l)
		goto find_first_elem;

	/* key was found, get next key in the same bucket */
	next_l = hlist_nulls_entry_safe(rcu_dereference_raw(hlist_nulls_next_rcu(&l->hash_node)),
				  struct htab_elem, hash_node);

	if (next_l) {
		/* if next elem in this hash list is non-zero, just return it */
		memcpy(next_key, next_l->key, key_size);
		return 0;
	}

	/* no more elements in this hash list, go to the next bucket */
	i = hash & (htab->n_buckets - 1);
	i++;

find_first_elem:
	/* iterate over buckets */
	for (; i < htab->n_buckets; i++) {
		head = select_bucket(htab, i);

		/* pick first element in the bucket */
		next_l = hlist_nulls_entry_safe(rcu_dereference_raw(hlist_nulls_first_rcu(head)),
					  struct htab_elem, hash_node);
		if (next_l) {
			/* if it's not empty, just return it */
			memcpy(next_key, next_l->key, key_size);
			return 0;
		}
	}

	/* iterated over all buckets and all elements */
	return -ENOENT;
}


htab_lru_percpu_map_lookup_batch(struct bpf_map *map,
				 const union bpf_attr *attr,
				 union bpf_attr __user *uattr)
{
	return __htab_map_lookup_and_delete_batch(map, attr, uattr, false,
						  true, true);
}


static void htab_free_elems(struct bpf_htab *htab)
{
	int i;

	if (!htab_is_percpu(htab))
		goto free_elems;

	for (i = 0; i < htab->map.max_entries; i++) {
		void __percpu *pptr;

		pptr = htab_elem_get_ptr(get_htab_elem(htab, i),
					 htab->map.key_size);
		free_percpu(pptr);
		cond_resched();
	}
free_elems:
	bpf_map_area_free(htab->elems);
}


static bool htab_is_lru(const struct bpf_htab *htab)
{
	return htab->map.map_type == BPF_MAP_TYPE_LRU_HASH ||
		htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH;
}


htab_map_lookup_batch(struct bpf_map *map, const union bpf_attr *attr,
		      union bpf_attr __user *uattr)
{
	return __htab_map_lookup_and_delete_batch(map, attr, uattr, false,
						  false, false);
}


htab_percpu_map_lookup_and_delete_batch(struct bpf_map *map,
					const union bpf_attr *attr,
					union bpf_attr __user *uattr)
{
	return __htab_map_lookup_and_delete_batch(map, attr, uattr, true,
						  false, true);
}


static void delete_all_elements(struct bpf_htab *htab)
{
	int i;

	for (i = 0; i < htab->n_buckets; i++) {
		struct hlist_nulls_head *head = select_bucket(htab, i);
		struct hlist_nulls_node *n;
		struct htab_elem *l;

		hlist_nulls_for_each_entry_safe(l, n, head, hash_node) {
			hlist_nulls_del_rcu(&l->hash_node);
			htab_elem_free(htab, l);
		}
	}
}


static void htab_percpu_map_seq_show_elem(struct bpf_map *map, void *key,
					  struct seq_file *m)
{
	struct htab_elem *l;
	void __percpu *pptr;
	int cpu;

	rcu_read_lock();

	l = __htab_map_lookup_elem(map, key);
	if (!l) {
		rcu_read_unlock();
		return;
	}

	btf_type_seq_show(map->btf, map->btf_key_type_id, key, m);
	seq_puts(m, ": {\n");
	pptr = htab_elem_get_ptr(l, map->key_size);
	for_each_possible_cpu(cpu) {
		seq_printf(m, "\tcpu%d: ", cpu);
		btf_type_seq_show(map->btf, map->btf_value_type_id,
				  per_cpu_ptr(pptr, cpu), m);
		seq_puts(m, "\n");
	}
	seq_puts(m, "}\n");

	rcu_read_unlock();
}