static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
			       u8 bdaddr_type, bdaddr_t *direct_addr,
			       u8 direct_addr_type, s8 rssi, u8 *data, u8 len)
{
	struct discovery_state *d = &hdev->discovery;
	struct smp_irk *irk;
	struct hci_conn *conn;
	bool match;
	u32 flags;
	u8 *ptr, real_len;

	switch (type) {
	case LE_ADV_IND:
	case LE_ADV_DIRECT_IND:
	case LE_ADV_SCAN_IND:
	case LE_ADV_NONCONN_IND:
	case LE_ADV_SCAN_RSP:
		break;
	default:
		bt_dev_err_ratelimited(hdev, "unknown advertising packet "
				       "type: 0x%02x", type);
		return;
	}

	/* Find the end of the data in case the report contains padded zero
	 * bytes at the end causing an invalid length value.
	 *
	 * When data is NULL, len is 0 so there is no need for extra ptr
	 * check as 'ptr < data + 0' is already false in such case.
	 */
	for (ptr = data; ptr < data + len && *ptr; ptr += *ptr + 1) {
		if (ptr + 1 + *ptr > data + len)
			break;
	}

	real_len = ptr - data;

	/* Adjust for actual length */
	if (len != real_len) {
		bt_dev_err_ratelimited(hdev, "advertising data len corrected %u -> %u",
				       len, real_len);
		len = real_len;
	}

	/* If the direct address is present, then this report is from
	 * a LE Direct Advertising Report event. In that case it is
	 * important to see if the address is matching the local
	 * controller address.
	 */
	if (direct_addr) {
		/* Only resolvable random addresses are valid for these
		 * kind of reports and others can be ignored.
		 */
		if (!hci_bdaddr_is_rpa(direct_addr, direct_addr_type))
			return;

		/* If the controller is not using resolvable random
		 * addresses, then this report can be ignored.
		 */
		if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
			return;

		/* If the local IRK of the controller does not match
		 * with the resolvable random address provided, then
		 * this report can be ignored.
		 */
		if (!smp_irk_matches(hdev, hdev->irk, direct_addr))
			return;
	}

	/* Check if we need to convert to identity address */
	irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
	if (irk) {
		bdaddr = &irk->bdaddr;
		bdaddr_type = irk->addr_type;
	}

	/* Check if we have been requested to connect to this device.
	 *
	 * direct_addr is set only for directed advertising reports (it is NULL
	 * for advertising reports) and is already verified to be RPA above.
	 */
	conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type,
								direct_addr);
	if (conn && type == LE_ADV_IND) {
		/* Store report for later inclusion by
		 * mgmt_device_connected
		 */
		memcpy(conn->le_adv_data, data, len);
		conn->le_adv_data_len = len;
	}

	/* Passive scanning shouldn't trigger any device found events,
	 * except for devices marked as CONN_REPORT for which we do send
	 * device found events, or advertisement monitoring requested.
	 */
	if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
		if (type == LE_ADV_DIRECT_IND)
			return;

		if (!hci_pend_le_action_lookup(&hdev->pend_le_reports,
					       bdaddr, bdaddr_type) &&
		    idr_is_empty(&hdev->adv_monitors_idr))
			return;

		if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND)
			flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
		else
			flags = 0;
		mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
				  rssi, flags, data, len, NULL, 0);
		return;
	}

	/* When receiving non-connectable or scannable undirected
	 * advertising reports, this means that the remote device is
	 * not connectable and then clearly indicate this in the
	 * device found event.
	 *
	 * When receiving a scan response, then there is no way to
	 * know if the remote device is connectable or not. However
	 * since scan responses are merged with a previously seen
	 * advertising report, the flags field from that report
	 * will be used.
	 *
	 * In the really unlikely case that a controller get confused
	 * and just sends a scan response event, then it is marked as
	 * not connectable as well.
	 */
	if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND ||
	    type == LE_ADV_SCAN_RSP)
		flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
	else
		flags = 0;

	/* If there's nothing pending either store the data from this
	 * event or send an immediate device found event if the data
	 * should not be stored for later.
	 */
	if (!has_pending_adv_report(hdev)) {
		/* If the report will trigger a SCAN_REQ store it for
		 * later merging.
		 */
		if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
			store_pending_adv_report(hdev, bdaddr, bdaddr_type,
						 rssi, flags, data, len);
			return;
		}

		mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
				  rssi, flags, data, len, NULL, 0);
		return;
	}

	/* Check if the pending report is for the same device as the new one */
	match = (!bacmp(bdaddr, &d->last_adv_addr) &&
		 bdaddr_type == d->last_adv_addr_type);

	/* If the pending data doesn't match this report or this isn't a
	 * scan response (e.g. we got a duplicate ADV_IND) then force
	 * sending of the pending data.
	 */
	if (type != LE_ADV_SCAN_RSP || !match) {
		/* Send out whatever is in the cache, but skip duplicates */
		if (!match)
			mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
					  d->last_adv_addr_type, NULL,
					  d->last_adv_rssi, d->last_adv_flags,
					  d->last_adv_data,
					  d->last_adv_data_len, NULL, 0);

		/* If the new report will trigger a SCAN_REQ store it for
		 * later merging.
		 */
		if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
			store_pending_adv_report(hdev, bdaddr, bdaddr_type,
						 rssi, flags, data, len);
			return;
		}

		/* The advertising reports cannot be merged, so clear
		 * the pending report and send out a device found event.
		 */
		clear_pending_adv_report(hdev);
		mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
				  rssi, flags, data, len, NULL, 0);
		return;
	}

	/* If we get here we've got a pending ADV_IND or ADV_SCAN_IND and
	 * the new event is a SCAN_RSP. We can therefore proceed with
	 * sending a merged device found event.
	 */
	mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
			  d->last_adv_addr_type, NULL, rssi, d->last_adv_flags,
			  d->last_adv_data, d->last_adv_data_len, data, len);
	clear_pending_adv_report(hdev);
}


static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force)
{
	struct io_rings *rings = ctx->rings;
	struct io_uring_cqe *cqe;
	struct io_kiocb *req;
	unsigned long flags;
	LIST_HEAD(list);

	if (!force) {
		if (list_empty_careful(&ctx->cq_overflow_list))
			return true;
		if ((ctx->cached_cq_tail - READ_ONCE(rings->cq.head) ==
		    rings->cq_ring_entries))
			return false;
	}

	spin_lock_irqsave(&ctx->completion_lock, flags);

	/* if force is set, the ring is going away. always drop after that */
	if (force)
		ctx->cq_overflow_flushed = 1;

	cqe = NULL;
	while (!list_empty(&ctx->cq_overflow_list)) {
		cqe = io_get_cqring(ctx);
		if (!cqe && !force)
			break;

		req = list_first_entry(&ctx->cq_overflow_list, struct io_kiocb,
						compl.list);
		list_move(&req->compl.list, &list);
		req->flags &= ~REQ_F_OVERFLOW;
		if (cqe) {
			WRITE_ONCE(cqe->user_data, req->user_data);
			WRITE_ONCE(cqe->res, req->result);
			WRITE_ONCE(cqe->flags, req->compl.cflags);
		} else {
			WRITE_ONCE(ctx->rings->cq_overflow,
				atomic_inc_return(&ctx->cached_cq_overflow));
		}
	}

	io_commit_cqring(ctx);
	io_cqring_mark_overflow(ctx);

	spin_unlock_irqrestore(&ctx->completion_lock, flags);
	io_cqring_ev_posted(ctx);

	while (!list_empty(&list)) {
		req = list_first_entry(&list, struct io_kiocb, compl.list);
		list_del(&req->compl.list);
		io_put_req(req);
	}

	return cqe != NULL;
}


static void io_prep_async_work(struct io_kiocb *req)
{
	const struct io_op_def *def = &io_op_defs[req->opcode];

	io_req_init_async(req);

	if (req->flags & REQ_F_ISREG) {
		if (def->hash_reg_file)
			io_wq_hash_work(&req->work, file_inode(req->file));
	} else {
		if (def->unbound_nonreg_file)
			req->work.flags |= IO_WQ_WORK_UNBOUND;
	}
	if (!req->work.mm && def->needs_mm) {
		mmgrab(current->mm);
		req->work.mm = current->mm;
	}
	if (!req->work.creds)
		req->work.creds = get_current_cred();
	if (!req->work.fs && def->needs_fs) {
		spin_lock(&current->fs->lock);
		if (!current->fs->in_exec) {
			req->work.fs = current->fs;
			req->work.fs->users++;
		} else {
			req->work.flags |= IO_WQ_WORK_CANCEL;
		}
		spin_unlock(&current->fs->lock);
	}
	if (def->needs_fsize)
		req->work.fsize = rlimit(RLIMIT_FSIZE);
	else
		req->work.fsize = RLIM_INFINITY;
}


static void io_async_task_func(struct callback_head *cb)
{
	struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work);
	struct async_poll *apoll = req->apoll;
	struct io_ring_ctx *ctx = req->ctx;

	trace_io_uring_task_run(req->ctx, req->opcode, req->user_data);

	if (io_poll_rewait(req, &apoll->poll)) {
		spin_unlock_irq(&ctx->completion_lock);
		percpu_ref_put(&ctx->refs);
		return;
	}

	/* If req is still hashed, it cannot have been canceled. Don't check. */
	if (hash_hashed(&req->hash_node))
		hash_del(&req->hash_node);

	io_poll_remove_double(req, apoll->double_poll);
	spin_unlock_irq(&ctx->completion_lock);

	if (!READ_ONCE(apoll->poll.canceled))
		__io_req_task_submit(req);
	else
		__io_req_task_cancel(req, -ECANCELED);

	percpu_ref_put(&ctx->refs);
	kfree(apoll->double_poll);
	kfree(apoll);
}


static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
			  struct file *ring_file, int ring_fd)
{
	struct io_submit_state state;
	struct io_kiocb *link = NULL;
	int i, submitted = 0;

	/* if we have a backlog and couldn't flush it all, return BUSY */
	if (test_bit(0, &ctx->sq_check_overflow)) {
		if (!list_empty(&ctx->cq_overflow_list) &&
		    !io_cqring_overflow_flush(ctx, false))
			return -EBUSY;
	}

	/* make sure SQ entry isn't read before tail */
	nr = min3(nr, ctx->sq_entries, io_sqring_entries(ctx));

	if (!percpu_ref_tryget_many(&ctx->refs, nr))
		return -EAGAIN;

	io_submit_state_start(&state, ctx, nr);

	ctx->ring_fd = ring_fd;
	ctx->ring_file = ring_file;

	for (i = 0; i < nr; i++) {
		const struct io_uring_sqe *sqe;
		struct io_kiocb *req;
		int err;

		sqe = io_get_sqe(ctx);
		if (unlikely(!sqe)) {
			io_consume_sqe(ctx);
			break;
		}
		req = io_alloc_req(ctx, &state);
		if (unlikely(!req)) {
			if (!submitted)
				submitted = -EAGAIN;
			break;
		}

		err = io_init_req(ctx, req, sqe, &state);
		io_consume_sqe(ctx);
		/* will complete beyond this point, count as submitted */
		submitted++;

		if (unlikely(err)) {
fail_req:
			io_put_req(req);
			io_req_complete(req, err);
			break;
		}

		trace_io_uring_submit_sqe(ctx, req->opcode, req->user_data,
						true, io_async_submit(ctx));
		err = io_submit_sqe(req, sqe, &link, &state.comp);
		if (err)
			goto fail_req;
	}

	if (unlikely(submitted != nr)) {
		int ref_used = (submitted == -EAGAIN) ? 0 : submitted;

		percpu_ref_put_many(&ctx->refs, nr - ref_used);
	}
	if (link)
		io_queue_link_head(link, &state.comp);
	io_submit_state_end(&state);

	 /* Commit SQ ring head once we've consumed and submitted all SQEs */
	io_commit_sqring(ctx);

	return submitted;
}


static void __io_free_req_finish(struct io_kiocb *req)
{
	struct io_ring_ctx *ctx = req->ctx;

	__io_put_req_task(req);
	if (likely(!io_is_fallback_req(req)))
		kmem_cache_free(req_cachep, req);
	else
		clear_bit_unlock(0, (unsigned long *) &ctx->fallback_req);
	percpu_ref_put(&ctx->refs);
}


static int io_openat2_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
	struct open_how __user *how;
	size_t len;
	int ret;

	if (req->flags & REQ_F_NEED_CLEANUP)
		return 0;
	how = u64_to_user_ptr(READ_ONCE(sqe->addr2));
	len = READ_ONCE(sqe->len);
	if (len < OPEN_HOW_SIZE_VER0)
		return -EINVAL;

	ret = copy_struct_from_user(&req->open.how, sizeof(req->open.how), how,
					len);
	if (ret)
		return ret;

	return __io_openat_prep(req, sqe);
}


static void io_get_req_task(struct io_kiocb *req)
{
	if (req->flags & REQ_F_TASK_PINNED)
		return;
	get_task_struct(req->task);
	req->flags |= REQ_F_TASK_PINNED;
}


static int io_write(struct io_kiocb *req, bool force_nonblock,
		    struct io_comp_state *cs)
{
	struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
	struct kiocb *kiocb = &req->rw.kiocb;
	struct iov_iter iter;
	size_t iov_count;
	ssize_t ret, ret2, io_size;
	unsigned long nr_segs;

	ret = io_import_iovec(WRITE, req, &iovec, &iter, !force_nonblock);
	if (ret < 0)
		return ret;
	io_size = ret;
	req->result = io_size;

	/* Ensure we clear previously set non-block flag */
	if (!force_nonblock)
		req->rw.kiocb.ki_flags &= ~IOCB_NOWAIT;

	/* If the file doesn't support async, just async punt */
	if (force_nonblock && !io_file_supports_async(req->file, WRITE))
		goto copy_iov;

	/* file path doesn't support NOWAIT for non-direct_IO */
	if (force_nonblock && !(kiocb->ki_flags & IOCB_DIRECT) &&
	    (req->flags & REQ_F_ISREG))
		goto copy_iov;

	iov_count = iov_iter_count(&iter);
	nr_segs = iter.nr_segs;
	ret = rw_verify_area(WRITE, req->file, &kiocb->ki_pos, iov_count);
	if (unlikely(ret))
		goto out_free;

	/*
	 * Open-code file_start_write here to grab freeze protection,
	 * which will be released by another thread in
	 * io_complete_rw().  Fool lockdep by telling it the lock got
	 * released so that it doesn't complain about the held lock when
	 * we return to userspace.
	 */
	if (req->flags & REQ_F_ISREG) {
		__sb_start_write(file_inode(req->file)->i_sb,
					SB_FREEZE_WRITE, true);
		__sb_writers_release(file_inode(req->file)->i_sb,
					SB_FREEZE_WRITE);
	}
	kiocb->ki_flags |= IOCB_WRITE;

	if (req->file->f_op->write_iter)
		ret2 = call_write_iter(req->file, kiocb, &iter);
	else if (req->file->f_op->write)
		ret2 = loop_rw_iter(WRITE, req->file, kiocb, &iter);
	else
		ret2 = -EINVAL;

	/*
	 * Raw bdev writes will return -EOPNOTSUPP for IOCB_NOWAIT. Just
	 * retry them without IOCB_NOWAIT.
	 */
	if (ret2 == -EOPNOTSUPP && (kiocb->ki_flags & IOCB_NOWAIT))
		ret2 = -EAGAIN;
	if (!force_nonblock || ret2 != -EAGAIN) {
		kiocb_done(kiocb, ret2, cs);
	} else {
		iter.count = iov_count;
		iter.nr_segs = nr_segs;
copy_iov:
		ret = io_setup_async_rw(req, io_size, iovec, inline_vecs,
					&iter);
		if (ret)
			goto out_free;
		/* it's copied and will be cleaned with ->io */
		iovec = NULL;
		return -EAGAIN;
	}
out_free:
	if (iovec)
		kfree(iovec);
	return ret;
}


static int sctp_setsockopt_scheduler_value(struct sock *sk,
					   char __user *optval,
					   unsigned int optlen)
{
	struct sctp_association *asoc;
	struct sctp_stream_value params;
	int retval = -EINVAL;

	if (optlen < sizeof(params))
		goto out;

	optlen = sizeof(params);
	if (copy_from_user(&params, optval, optlen)) {
		retval = -EFAULT;
		goto out;
	}

	asoc = sctp_id2assoc(sk, params.assoc_id);
	if (!asoc)
		goto out;

	retval = sctp_sched_set_value(asoc, params.stream_id,
				      params.stream_value, GFP_KERNEL);

out:
	return retval;
}


static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
				       void (*cb)(struct sctp_chunk *))

{
	struct sctp_outq *q = &asoc->outqueue;
	struct sctp_transport *t;
	struct sctp_chunk *chunk;

	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
			cb(chunk);

	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
		cb(chunk);

	list_for_each_entry(chunk, &q->sacked, transmitted_list)
		cb(chunk);

	list_for_each_entry(chunk, &q->abandoned, transmitted_list)
		cb(chunk);

	list_for_each_entry(chunk, &q->out_chunk_list, list)
		cb(chunk);
}


static int sctp_setsockopt_auth_key(struct sock *sk,
				    char __user *optval,
				    unsigned int optlen)
{
	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
	struct sctp_authkey *authkey;
	struct sctp_association *asoc;
	int ret;

	if (!ep->auth_enable)
		return -EACCES;

	if (optlen <= sizeof(struct sctp_authkey))
		return -EINVAL;
	/* authkey->sca_keylength is u16, so optlen can't be bigger than
	 * this.
	 */
	optlen = min_t(unsigned int, optlen, USHRT_MAX +
					     sizeof(struct sctp_authkey));

	authkey = memdup_user(optval, optlen);
	if (IS_ERR(authkey))
		return PTR_ERR(authkey);

	if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) {
		ret = -EINVAL;
		goto out;
	}

	asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
	if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
		ret = -EINVAL;
		goto out;
	}

	ret = sctp_auth_set_key(ep, asoc, authkey);
out:
	kzfree(authkey);
	return ret;
}


int xfrm_state_delete(struct xfrm_state *x)
{
	int err;

	spin_lock_bh(&x->lock);
	err = __xfrm_state_delete(x);
	spin_unlock_bh(&x->lock);

	return err;
}


xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi,
		  u8 proto, unsigned short family)
{
	struct xfrm_state *x;

	rcu_read_lock();
	x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
	rcu_read_unlock();
	return x;
}


static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
	struct net *net = sock_net(sk);
	unsigned int dir;
	int err = 0, delete;
	struct sadb_x_policy *pol;
	struct xfrm_policy *xp;
	struct km_event c;

	if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
		return -EINVAL;

	dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
	if (dir >= XFRM_POLICY_MAX)
		return -EINVAL;

	delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
	xp = xfrm_policy_byid(net, DUMMY_MARK, 0, XFRM_POLICY_TYPE_MAIN,
			      dir, pol->sadb_x_policy_id, delete, &err);
	if (xp == NULL)
		return -ENOENT;

	if (delete) {
		xfrm_audit_policy_delete(xp, err ? 0 : 1, true);

		if (err)
			goto out;
		c.seq = hdr->sadb_msg_seq;
		c.portid = hdr->sadb_msg_pid;
		c.data.byid = 1;
		c.event = XFRM_MSG_DELPOLICY;
		km_policy_notify(xp, dir, &c);
	} else {
		err = key_pol_get_resp(sk, xp, hdr, dir);
	}

out:
	xfrm_pol_put(xp);
	return err;
}


static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
					      int add_keys, int hsc)
{
	struct sk_buff *skb;
	struct sadb_msg *hdr;
	struct sadb_sa *sa;
	struct sadb_lifetime *lifetime;
	struct sadb_address *addr;
	struct sadb_key *key;
	struct sadb_x_sa2 *sa2;
	struct sadb_x_sec_ctx *sec_ctx;
	struct xfrm_sec_ctx *xfrm_ctx;
	int ctx_size = 0;
	int size;
	int auth_key_size = 0;
	int encrypt_key_size = 0;
	int sockaddr_size;
	struct xfrm_encap_tmpl *natt = NULL;
	int mode;

	/* address family check */
	sockaddr_size = pfkey_sockaddr_size(x->props.family);
	if (!sockaddr_size)
		return ERR_PTR(-EINVAL);

	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
	   key(AE), (identity(SD),) (sensitivity)> */
	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
		sizeof(struct sadb_lifetime) +
		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
			sizeof(struct sadb_address)*2 +
				sockaddr_size*2 +
					sizeof(struct sadb_x_sa2);

	if ((xfrm_ctx = x->security)) {
		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
	}

	/* identity & sensitivity */
	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family))
		size += sizeof(struct sadb_address) + sockaddr_size;

	if (add_keys) {
		if (x->aalg && x->aalg->alg_key_len) {
			auth_key_size =
				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
			size += sizeof(struct sadb_key) + auth_key_size;
		}
		if (x->ealg && x->ealg->alg_key_len) {
			encrypt_key_size =
				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
			size += sizeof(struct sadb_key) + encrypt_key_size;
		}
	}
	if (x->encap)
		natt = x->encap;

	if (natt && natt->encap_type) {
		size += sizeof(struct sadb_x_nat_t_type);
		size += sizeof(struct sadb_x_nat_t_port);
		size += sizeof(struct sadb_x_nat_t_port);
	}

	skb =  alloc_skb(size + 16, GFP_ATOMIC);
	if (skb == NULL)
		return ERR_PTR(-ENOBUFS);

	/* call should fill header later */
	hdr = skb_put(skb, sizeof(struct sadb_msg));
	memset(hdr, 0, size);	/* XXX do we need this ? */
	hdr->sadb_msg_len = size / sizeof(uint64_t);

	/* sa */
	sa = skb_put(skb, sizeof(struct sadb_sa));
	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
	sa->sadb_sa_exttype = SADB_EXT_SA;
	sa->sadb_sa_spi = x->id.spi;
	sa->sadb_sa_replay = x->props.replay_window;
	switch (x->km.state) {
	case XFRM_STATE_VALID:
		sa->sadb_sa_state = x->km.dying ?
			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
		break;
	case XFRM_STATE_ACQ:
		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
		break;
	default:
		sa->sadb_sa_state = SADB_SASTATE_DEAD;
		break;
	}
	sa->sadb_sa_auth = 0;
	if (x->aalg) {
		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
		sa->sadb_sa_auth = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	sa->sadb_sa_encrypt = 0;
	BUG_ON(x->ealg && x->calg);
	if (x->ealg) {
		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
	if (x->calg) {
		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}

	sa->sadb_sa_flags = 0;
	if (x->props.flags & XFRM_STATE_NOECN)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;

	/* hard time */
	if (hsc & 2) {
		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
	}
	/* soft time */
	if (hsc & 1) {
		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
	}
	/* current time */
	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
	lifetime->sadb_lifetime_len =
		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
	lifetime->sadb_lifetime_allocations = x->curlft.packets;
	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
	/* src address */
	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	/* "if the ports are non-zero, then the sadb_address_proto field,
	   normally zero, MUST be filled in with the transport
	   protocol's number." - RFC2367 */
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->props.saddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	if (!addr->sadb_address_prefixlen)
		BUG();

	/* dst address */
	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->id.daddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	if (!addr->sadb_address_prefixlen)
		BUG();

	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr,
			     x->props.family)) {
		addr = skb_put(skb,
			       sizeof(struct sadb_address) + sockaddr_size);
		addr->sadb_address_len =
			(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
		addr->sadb_address_proto =
			pfkey_proto_from_xfrm(x->sel.proto);
		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
		addr->sadb_address_reserved = 0;

		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	}

	/* auth key */
	if (add_keys && auth_key_size) {
		key = skb_put(skb, sizeof(struct sadb_key) + auth_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
			sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
		key->sadb_key_bits = x->aalg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
	}
	/* encrypt key */
	if (add_keys && encrypt_key_size) {
		key = skb_put(skb, sizeof(struct sadb_key) + encrypt_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) +
				     encrypt_key_size) / sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
		key->sadb_key_bits = x->ealg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->ealg->alg_key,
		       (x->ealg->alg_key_len+7)/8);
	}

	/* sa */
	sa2 = skb_put(skb, sizeof(struct sadb_x_sa2));
	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
		kfree_skb(skb);
		return ERR_PTR(-EINVAL);
	}
	sa2->sadb_x_sa2_mode = mode;
	sa2->sadb_x_sa2_reserved1 = 0;
	sa2->sadb_x_sa2_reserved2 = 0;
	sa2->sadb_x_sa2_sequence = 0;
	sa2->sadb_x_sa2_reqid = x->props.reqid;

	if (natt && natt->encap_type) {
		struct sadb_x_nat_t_type *n_type;
		struct sadb_x_nat_t_port *n_port;

		/* type */
		n_type = skb_put(skb, sizeof(*n_type));
		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
		n_type->sadb_x_nat_t_type_type = natt->encap_type;
		n_type->sadb_x_nat_t_type_reserved[0] = 0;
		n_type->sadb_x_nat_t_type_reserved[1] = 0;
		n_type->sadb_x_nat_t_type_reserved[2] = 0;

		/* source port */
		n_port = skb_put(skb, sizeof(*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
		n_port->sadb_x_nat_t_port_reserved = 0;

		/* dest port */
		n_port = skb_put(skb, sizeof(*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
		n_port->sadb_x_nat_t_port_reserved = 0;
	}

	/* security context */
	if (xfrm_ctx) {
		sec_ctx = skb_put(skb,
				  sizeof(struct sadb_x_sec_ctx) + ctx_size);
		sec_ctx->sadb_x_sec_len =
		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
		       xfrm_ctx->ctx_len);
	}

	return skb;
}


struct xfrm_state_afinfo *xfrm_state_afinfo_get_rcu(unsigned int family)
{
	if (unlikely(family >= NPROTO))
		return NULL;

	return rcu_dereference(xfrm_state_afinfo[family]);
}


int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
{
	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
	const struct xfrm_type **typemap;
	int err = 0;

	if (unlikely(afinfo == NULL))
		return -EAFNOSUPPORT;
	typemap = afinfo->type_map;
	spin_lock_bh(&xfrm_type_lock);

	if (likely(typemap[type->proto] == NULL))
		typemap[type->proto] = type;
	else
		err = -EEXIST;
	spin_unlock_bh(&xfrm_type_lock);
	rcu_read_unlock();
	return err;
}


static inline struct net *xs_net(struct xfrm_state *x)
{
	return read_pnet(&x->xs_net);
}


static inline int xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
				      unsigned int family)
{
	return __xfrm_decode_session(skb, fl, family, 0);
}


static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
{
 	/* should not happen */
 	kfree_skb(skb);
	return 0;
}


static void xfrm_put_mode(struct xfrm_mode *mode)
{
	module_put(mode->owner);
}


__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
{
	struct net *net = xs_net(x);
	u32 mark = x->mark.v & x->mark.m;

	if (use_spi)
		return __xfrm_state_lookup(net, mark, &x->id.daddr,
					   x->id.spi, x->id.proto, family);
	else
		return __xfrm_state_lookup_byaddr(net, mark,
						  &x->id.daddr,
						  &x->props.saddr,
						  x->id.proto, family);
}


int xfrm_register_km(struct xfrm_mgr *km)
{
	spin_lock_bh(&xfrm_km_lock);
	list_add_tail_rcu(&km->list, &xfrm_km_list);
	spin_unlock_bh(&xfrm_km_lock);
	return 0;
}


static inline int xfrm_decode_session_reverse(struct sk_buff *skb,
					      struct flowi *fl,
					      unsigned int family)
{
	return __xfrm_decode_session(skb, fl, family, 1);
}


static inline void xfrm_pol_hold(struct xfrm_policy *policy)
{
	if (likely(policy != NULL))
		refcount_inc(&policy->refcnt);
}


static void xfrm_replay_timer_handler(struct timer_list *t)
{
	struct xfrm_state *x = from_timer(x, t, rtimer);

	spin_lock(&x->lock);

	if (x->km.state == XFRM_STATE_VALID) {
		if (xfrm_aevent_is_on(xs_net(x)))
			x->repl->notify(x, XFRM_REPLAY_TIMEOUT);
		else
			x->xflags |= XFRM_TIME_DEFER;
	}

	spin_unlock(&x->lock);
}


int xfrm_unregister_km(struct xfrm_mgr *km)
{
	spin_lock_bh(&xfrm_km_lock);
	list_del_rcu(&km->list);
	spin_unlock_bh(&xfrm_km_lock);
	synchronize_rcu();
	return 0;
}


static inline void xfrm_sk_free_policy(struct sock *sk) {}


static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir,
					     struct sk_buff *skb)
{
	return __xfrm_policy_check2(sk, dir, skb, AF_INET6, 1);
}


void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
			       __be32 net_spi, __be32 net_seq)
{
	struct audit_buffer *audit_buf;
	u32 spi;

	audit_buf = xfrm_audit_start("SA-notfound");
	if (audit_buf == NULL)
		return;
	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
	spi = ntohl(net_spi);
	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
			 spi, spi, ntohl(net_seq));
	audit_log_end(audit_buf);
}


static inline int xfrm6_route_forward(struct sk_buff *skb)
{
	return xfrm_route_forward(skb, AF_INET6);
}


static inline struct dst_entry *xfrm_dst_path(const struct dst_entry *dst)
{
#ifdef CONFIG_XFRM
	if (dst->xfrm) {
		const struct xfrm_dst *xdst = (const struct xfrm_dst *) dst;

		return xdst->path;
	}
#endif
	return (struct dst_entry *) dst;
}


static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
{
	xfrm_pols_put(xdst->pols, xdst->num_pols);
	dst_release(xdst->route);
	if (likely(xdst->u.dst.xfrm))
		xfrm_state_put(xdst->u.dst.xfrm);
}


int xfrm_register_mode(struct xfrm_mode *mode, int family)
{
	struct xfrm_state_afinfo *afinfo;
	struct xfrm_mode **modemap;
	int err;

	if (unlikely(mode->encap >= XFRM_MODE_MAX))
		return -EINVAL;

	afinfo = xfrm_state_get_afinfo(family);
	if (unlikely(afinfo == NULL))
		return -EAFNOSUPPORT;

	err = -EEXIST;
	modemap = afinfo->mode_map;
	spin_lock_bh(&xfrm_mode_lock);
	if (modemap[mode->encap])
		goto out;

	err = -ENOENT;
	if (!try_module_get(afinfo->owner))
		goto out;

	mode->afinfo = afinfo;
	modemap[mode->encap] = mode;
	err = 0;

out:
	spin_unlock_bh(&xfrm_mode_lock);
	rcu_read_unlock();
	return err;
}


static inline bool xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_ctx *s2)
{
	return true;
}


static inline unsigned int xfrm_src_hash(struct net *net,
					 const xfrm_address_t *daddr,
					 const xfrm_address_t *saddr,
					 unsigned short family)
{
	return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
}


static int __xfrm6_tunnel_spi_check(struct net *net, u32 spi)
{
	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
	struct xfrm6_tunnel_spi *x6spi;
	int index = xfrm6_tunnel_spi_hash_byspi(spi);

	hlist_for_each_entry(x6spi,
			     &xfrm6_tn->spi_byspi[index],
			     list_byspi) {
		if (x6spi->spi == spi)
			return -1;
	}
	return index;
}


static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
{
	int i;
	for (i = npols - 1; i >= 0; --i)
		xfrm_pol_put(pols[i]);
}


struct mbuf *m_dup(Slirp *slirp, struct mbuf *m,
                   bool copy_header,
                   size_t header_size)
{
    struct mbuf *n;
    int mcopy_result;

    /* The previous mbuf was supposed to have it already, we can check it along
     * the way */
    assert(M_ROOMBEFORE(m) >= header_size);

    n = m_get(slirp);
    m_inc(n, m->m_len + header_size);

    if (copy_header) {
        m->m_len += header_size;
        m->m_data -= header_size;
        mcopy_result = m_copy(n, m, 0, m->m_len + header_size);
        n->m_data += header_size;
        m->m_len -= header_size;
        m->m_data += header_size;
    } else {
        n->m_data += header_size;
        mcopy_result = m_copy(n, m, 0, m->m_len);
    }
    g_assert(mcopy_result == 0);

    return n;
}


static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
                              struct tftphdr *hdr)
{
    int s;

    s = tftp_session_find(slirp, srcsas, hdr);

    if (s < 0) {
        return;
    }

    tftp_session_terminate(&slirp->tftp_sessions[s]);
}


static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
{
  if (svg_info->size != (char *) NULL)
    svg_info->size=DestroyString(svg_info->size);
  if (svg_info->text != (char *) NULL)
    svg_info->text=DestroyString(svg_info->text);
  if (svg_info->scale != (double *) NULL)
    svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
  if (svg_info->title != (char *) NULL)
    svg_info->title=DestroyString(svg_info->title);
  if (svg_info->comment != (char *) NULL)
    svg_info->comment=DestroyString(svg_info->comment);
  return((SVGInfo *) RelinquishMagickMemory(svg_info));
}


static void intel_hda_corb_run(IntelHDAState *d)
{
    hwaddr addr;
    uint32_t rp, verb;

    if (d->ics & ICH6_IRS_BUSY) {
        dprint(d, 2, "%s: [icw] verb 0x%08x\n", __func__, d->icw);
        intel_hda_send_command(d, d->icw);
        return;
    }

    for (;;) {
        if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
            dprint(d, 2, "%s: !run\n", __func__);
            return;
        }
        if ((d->corb_rp & 0xff) == d->corb_wp) {
            dprint(d, 2, "%s: corb ring empty\n", __func__);
            return;
        }
        if (d->rirb_count == d->rirb_cnt) {
            dprint(d, 2, "%s: rirb count reached\n", __func__);
            return;
        }

        rp = (d->corb_rp + 1) & 0xff;
        addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
        ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED);
        d->corb_rp = rp;

        dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb);
        intel_hda_send_command(d, verb);
    }
}


static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
{
    if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
        intel_hda_reset(DEVICE(d));
    }
}


void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size,
                        hda_codec_response_func response,
                        hda_codec_xfer_func xfer)
{
    qbus_init(bus, bus_size, TYPE_HDA_BUS, dev, NULL);
    bus->response = response;
    bus->xfer = xfer;
}


static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
{
    const MemTxAttrs attrs = { .memory = true };
    HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
    IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
    hwaddr addr;
    uint32_t wp, ex;
    MemTxResult res = MEMTX_OK;

    if (d->ics & ICH6_IRS_BUSY) {
        dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n",
               __func__, response, dev->cad);
        d->irr = response;
        d->ics &= ~(ICH6_IRS_BUSY | 0xf0);
        d->ics |= (ICH6_IRS_VALID | (dev->cad << 4));
        return;
    }

    if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) {
        dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __func__);
        return;
    }

    ex = (solicited ? 0 : (1 << 4)) | dev->cad;
    wp = (d->rirb_wp + 1) & 0xff;
    addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
    res |= stl_le_pci_dma(&d->pci, addr + 8 * wp, response, attrs);
    res |= stl_le_pci_dma(&d->pci, addr + 8 * wp + 4, ex, attrs);
    if (res != MEMTX_OK && (d->rirb_ctl & ICH6_RBCTL_OVERRUN_EN)) {
        d->rirb_sts |= ICH6_RBSTS_OVERRUN;
        intel_hda_update_irq(d);
    }
    d->rirb_wp = wp;

    dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
           __func__, wp, response, ex);

    d->rirb_count++;
    if (d->rirb_count == d->rirb_cnt) {
        dprint(d, 2, "%s: rirb count reached (%d)\n", __func__, d->rirb_count);
        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
            d->rirb_sts |= ICH6_RBSTS_IRQ;
            intel_hda_update_irq(d);
        }
    } else if ((d->corb_rp & 0xff) == d->corb_wp) {
        dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __func__,
               d->rirb_count, d->rirb_cnt);
        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
            d->rirb_sts |= ICH6_RBSTS_IRQ;
            intel_hda_update_irq(d);
        }
    }
}


static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
{
    intel_hda_update_irq(d);

    if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
        /* cleared ICH6_RBSTS_IRQ */
        d->rirb_count = 0;
        intel_hda_corb_run(d);
    }
}


static struct ucma_context *ucma_get_ctx(struct ucma_file *file, int id)
{
	struct ucma_context *ctx;

	xa_lock(&ctx_table);
	ctx = _ucma_find_context(id, file);
	if (!IS_ERR(ctx))
		if (!refcount_inc_not_zero(&ctx->ref))
			ctx = ERR_PTR(-ENXIO);
	xa_unlock(&ctx_table);
	return ctx;
}


static ssize_t ucma_reject(struct ucma_file *file, const char __user *inbuf,
			   int in_len, int out_len)
{
	struct rdma_ucm_reject cmd;
	struct ucma_context *ctx;
	int ret;

	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
		return -EFAULT;

	if (!cmd.reason)
		cmd.reason = IB_CM_REJ_CONSUMER_DEFINED;

	switch (cmd.reason) {
	case IB_CM_REJ_CONSUMER_DEFINED:
	case IB_CM_REJ_VENDOR_OPTION_NOT_SUPPORTED:
		break;
	default:
		return -EINVAL;
	}

	ctx = ucma_get_ctx_dev(file, cmd.id);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);

	mutex_lock(&ctx->mutex);
	ret = rdma_reject(ctx->cm_id, cmd.private_data, cmd.private_data_len,
			  cmd.reason);
	mutex_unlock(&ctx->mutex);
	ucma_put_ctx(ctx);
	return ret;
}


static int __destroy_id(struct ucma_context *ctx)
{
	/*
	 * If the refcount is already 0 then ucma_close_id() has already
	 * destroyed the cm_id, otherwise holding the refcount keeps cm_id
	 * valid. Prevent queue_work() from being called.
	 */
	if (refcount_inc_not_zero(&ctx->ref)) {
		rdma_lock_handler(ctx->cm_id);
		ctx->destroying = 1;
		rdma_unlock_handler(ctx->cm_id);
		ucma_put_ctx(ctx);
	}

	cancel_work_sync(&ctx->close_work);
	/* At this point it's guaranteed that there is no inflight closing task */
	if (ctx->cm_id)
		ucma_close_id(&ctx->close_work);
	return ucma_free_ctx(ctx);
}


static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
			      int in_len, int out_len)
{
	struct rdma_ucm_create_id cmd;
	struct rdma_ucm_create_id_resp resp;
	struct ucma_context *ctx;
	struct rdma_cm_id *cm_id;
	enum ib_qp_type qp_type;
	int ret;

	if (out_len < sizeof(resp))
		return -ENOSPC;

	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
		return -EFAULT;

	ret = ucma_get_qp_type(&cmd, &qp_type);
	if (ret)
		return ret;

	ctx = ucma_alloc_ctx(file);
	if (!ctx)
		return -ENOMEM;

	ctx->uid = cmd.uid;
	cm_id = __rdma_create_id(current->nsproxy->net_ns,
				 ucma_event_handler, ctx, cmd.ps, qp_type, NULL);
	if (IS_ERR(cm_id)) {
		ret = PTR_ERR(cm_id);
		goto err1;
	}
	ctx->cm_id = cm_id;

	resp.id = ctx->id;
	if (copy_to_user(u64_to_user_ptr(cmd.response),
			 &resp, sizeof(resp))) {
		xa_erase(&ctx_table, ctx->id);
		__destroy_id(ctx);
		return -EFAULT;
	}

	mutex_lock(&file->mut);
	ucma_finish_ctx(ctx);
	mutex_unlock(&file->mut);
	return 0;

err1:
	xa_erase(&ctx_table, ctx->id);
	kfree(ctx);
	return ret;
}


static void ucma_cleanup_multicast(struct ucma_context *ctx)
{
	struct ucma_multicast *mc;
	unsigned long index;

	xa_for_each(&multicast_table, index, mc) {
		if (mc->ctx != ctx)
			continue;
		/*
		 * At this point mc->ctx->ref is 0 so the mc cannot leave the
		 * lock on the reader and this is enough serialization
		 */
		xa_erase(&multicast_table, index);
		kfree(mc);
	}
}


static ssize_t ucma_migrate_id(struct ucma_file *new_file,
			       const char __user *inbuf,
			       int in_len, int out_len)
{
	struct rdma_ucm_migrate_id cmd;
	struct rdma_ucm_migrate_resp resp;
	struct ucma_event *uevent, *tmp;
	struct ucma_context *ctx;
	LIST_HEAD(event_list);
	struct fd f;
	struct ucma_file *cur_file;
	int ret = 0;

	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
		return -EFAULT;

	/* Get current fd to protect against it being closed */
	f = fdget(cmd.fd);
	if (!f.file)
		return -ENOENT;
	if (f.file->f_op != &ucma_fops) {
		ret = -EINVAL;
		goto file_put;
	}
	cur_file = f.file->private_data;

	/* Validate current fd and prevent destruction of id. */
	ctx = ucma_get_ctx(cur_file, cmd.id);
	if (IS_ERR(ctx)) {
		ret = PTR_ERR(ctx);
		goto file_put;
	}

	rdma_lock_handler(ctx->cm_id);
	/*
	 * ctx->file can only be changed under the handler & xa_lock. xa_load()
	 * must be checked again to ensure the ctx hasn't begun destruction
	 * since the ucma_get_ctx().
	 */
	xa_lock(&ctx_table);
	if (_ucma_find_context(cmd.id, cur_file) != ctx) {
		xa_unlock(&ctx_table);
		ret = -ENOENT;
		goto err_unlock;
	}
	ctx->file = new_file;
	xa_unlock(&ctx_table);

	mutex_lock(&cur_file->mut);
	list_del(&ctx->list);
	/*
	 * At this point lock_handler() prevents addition of new uevents for
	 * this ctx.
	 */
	list_for_each_entry_safe(uevent, tmp, &cur_file->event_list, list)
		if (uevent->ctx == ctx)
			list_move_tail(&uevent->list, &event_list);
	resp.events_reported = ctx->events_reported;
	mutex_unlock(&cur_file->mut);

	mutex_lock(&new_file->mut);
	list_add_tail(&ctx->list, &new_file->ctx_list);
	list_splice_tail(&event_list, &new_file->event_list);
	mutex_unlock(&new_file->mut);

	if (copy_to_user(u64_to_user_ptr(cmd.response),
			 &resp, sizeof(resp)))
		ret = -EFAULT;

err_unlock:
	rdma_unlock_handler(ctx->cm_id);
	ucma_put_ctx(ctx);
file_put:
	fdput(f);
	return ret;
}


static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf,
			   int in_len, int out_len)
{
	struct rdma_ucm_notify cmd;
	struct ucma_context *ctx;
	int ret = -EINVAL;

	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
		return -EFAULT;

	ctx = ucma_get_ctx(file, cmd.id);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);

	mutex_lock(&ctx->mutex);
	if (ctx->cm_id->device)
		ret = rdma_notify(ctx->cm_id, (enum ib_event_type)cmd.event);
	mutex_unlock(&ctx->mutex);

	ucma_put_ctx(ctx);
	return ret;
}


init_pluginsys(void) {

	if (StonithPIsys) {
		return TRUE;
	}

	/* PILpisysSetDebugLevel(10); */
	StonithPIsys = NewPILPluginUniv(STONITH_MODULES);

	if (StonithPIsys) {
		int rc = PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs);
		if (rc != PIL_OK) {
			fprintf(stderr, "generic plugin load failed: %d\n", rc);
			DelPILPluginUniv(StonithPIsys);
			StonithPIsys = NULL;
		}
		/*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/
	}else{
		fprintf(stderr, "pi univ creation failed\n");
	}
	return StonithPIsys != NULL;
}


confhelp(const char * cmd, FILE* stream, const char * devtype)
{
	char ** typelist;
	char ** this;
	Stonith *       s;
	int	devfound = 0;

	/* non-NULL devtype indicates help for specific device, so no header */
	if (devtype == NULL) {
		fprintf(stream
		,	"\nSTONITH -t device types and"
			" associated configuration details:\n");
	}

	typelist = stonith_types();

	if (typelist == NULL) {
		fprintf(stderr,
			"Failed to retrieve list of STONITH modules!\n");
		return;
	}
	for(this=typelist; *this && !devfound; ++this) {
		const char *    SwitchType = *this;
		const char *	cres;
		const char **	pnames;

		if ((s = stonith_new(SwitchType)) == NULL) {
			fprintf(stderr, "Invalid STONITH type %s(!)\n"
			,	SwitchType);
			continue;
		}

		if (devtype) {
			if (strcmp(devtype, SwitchType)) {
				continue;
			} else {
				devfound = 1;
			}
		}

		fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType);

		if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){
			fprintf(stream, "%s\n"
			,	cres);
		}

		if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){
			fprintf(stream
			,	"For more information see %s\n"
			,	cres);
		}
		if (NULL == (pnames = stonith_get_confignames(s))) {
			continue;
		}
		fprintf(stream
		,	"List of valid parameter names for %s STONITH device:\n"
		,	SwitchType);
		for (;*pnames; ++pnames) {
			fprintf(stream
			,	"\t%s\n", *pnames);
		}

#ifdef ST_CONFI_INFO_SYNTAX
		fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n"
		,    SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX));
#else
		fprintf(stream, "For Config info [-p] syntax"
		", give each of the above parameters in order as"
		"\nthe -p value.\n"
		"Arguments are separated by white space.");
#endif
#ifdef ST_CONFI_FILE_SYNTAX
		fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n"
		,    SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX));
#else
		fprintf(stream
		,	"\nConfig file [-F] syntax is the same as -p"
		", except # at the start of a line"
		"\ndenotes a comment\n");
#endif

		stonith_delete(s); s = NULL;
	}
	/* Note that the type list can't/shouldn't be freed */
	if (devtype && !devfound) {
		fprintf(stderr, "Invalid device type: '%s'\n", devtype);
	}

}


free_NVpair(StonithNVpair* nv)
{
	StonithNVpair* this;

	if (nv == NULL) {
		return;
	}
	for (this=nv; this->s_name; ++this) {
		FREE(this->s_name);
		if (this->s_value) {
			FREE(this->s_value);
		}
	}
	FREE(nv);
}


stonith_set_config(Stonith* s, StonithNVpair* list)
{
	StonithPlugin*	sp = (StonithPlugin*)s;

	if (sp && sp->s_ops) {
		int	rc = sp->s_ops->set_config(sp, list);
		if (rc == S_OK) {
			sp->isconfigured = TRUE;
		}
		return rc;
	}
	return S_INVAL;
}


stonith_get_confignames(Stonith* s)
{
	StonithPlugin*	sp = (StonithPlugin*)s;

	if (sp && sp->s_ops) {
		return sp->s_ops->get_confignames(sp);
	}
	return NULL;
}


Mat_VarCreate(const char *name,enum matio_classes class_type,
    enum matio_types data_type,int rank,size_t *dims,void *data,int opt)
{
    size_t nelems = 1, data_size;
    matvar_t *matvar = NULL;
    int j, err;

    if ( dims == NULL )
        return NULL;

    matvar = Mat_VarCalloc();
    if ( NULL == matvar )
        return NULL;

    matvar->compression = MAT_COMPRESSION_NONE;
    matvar->isComplex   = opt & MAT_F_COMPLEX;
    matvar->isGlobal    = opt & MAT_F_GLOBAL;
    matvar->isLogical   = opt & MAT_F_LOGICAL;
    if ( name )
        matvar->name = strdup_printf("%s",name);
    matvar->rank = rank;
    matvar->dims = (size_t*)malloc(matvar->rank*sizeof(*matvar->dims));
    for ( j = 0; j < matvar->rank; j++ ) {
        matvar->dims[j] = dims[j];
        nelems *= dims[j];
    }
    matvar->class_type = class_type;
    matvar->data_type  = data_type;
    switch ( data_type ) {
        case MAT_T_INT8:
            data_size = 1;
            break;
        case MAT_T_UINT8:
            data_size = 1;
            break;
        case MAT_T_INT16:
            data_size = 2;
            break;
        case MAT_T_UINT16:
            data_size = 2;
            break;
        case MAT_T_INT64:
            data_size = 8;
            break;
        case MAT_T_UINT64:
            data_size = 8;
            break;
        case MAT_T_INT32:
            data_size = 4;
            break;
        case MAT_T_UINT32:
            data_size = 4;
            break;
        case MAT_T_SINGLE:
            data_size = sizeof(float);
            break;
        case MAT_T_DOUBLE:
            data_size = sizeof(double);
            break;
        case MAT_T_UTF8:
            data_size = 1;
            break;
        case MAT_T_UTF16:
            data_size = 2;
            break;
        case MAT_T_UTF32:
            data_size = 4;
            break;
        case MAT_T_CELL:
            data_size = sizeof(matvar_t **);
            break;
        case MAT_T_STRUCT:
        {
            data_size = sizeof(matvar_t **);
            if ( data != NULL ) {
                matvar_t **fields = (matvar_t**)data;
                size_t nfields = 0;
                while ( fields[nfields] != NULL )
                    nfields++;
                if ( nelems )
                    nfields /= nelems;
                matvar->internal->num_fields = nfields;
                if ( nfields ) {
                    size_t i;
                    matvar->internal->fieldnames =
                        (char**)calloc(nfields,sizeof(*matvar->internal->fieldnames));
                    for ( i = 0; i < nfields; i++ )
                        matvar->internal->fieldnames[i] = strdup(fields[i]->name);
                    err = SafeMul(&nelems, nelems, nfields);
                    if ( err ) {
                        Mat_VarFree(matvar);
                        Mat_Critical("Integer multiplication overflow");
                        return NULL;
                    }
                }
            }
            break;
        }
        default:
            Mat_VarFree(matvar);
            Mat_Critical("Unrecognized data_type");
            return NULL;
    }
    if ( matvar->class_type == MAT_C_SPARSE ) {
        matvar->data_size = sizeof(mat_sparse_t);
        matvar->nbytes    = matvar->data_size;
    } else {
        matvar->data_size = data_size;
        err = SafeMul(&matvar->nbytes, nelems, matvar->data_size);
        if ( err ) {
            Mat_VarFree(matvar);
            Mat_Critical("Integer multiplication overflow");
            return NULL;
        }
    }
    if ( data == NULL ) {
        if ( MAT_C_CELL == matvar->class_type && nelems > 0 )
            matvar->data = calloc(nelems,sizeof(matvar_t*));
        else
            matvar->data = NULL;
    } else if ( opt & MAT_F_DONT_COPY_DATA ) {
        matvar->data         = data;
        matvar->mem_conserve = 1;
    } else if ( MAT_C_SPARSE == matvar->class_type ) {
        mat_sparse_t *sparse_data, *sparse_data_in;

        sparse_data_in = (mat_sparse_t*)data;
        sparse_data    = (mat_sparse_t*)malloc(sizeof(mat_sparse_t));
        if ( NULL != sparse_data ) {
            sparse_data->nzmax = sparse_data_in->nzmax;
            sparse_data->nir   = sparse_data_in->nir;
            sparse_data->njc   = sparse_data_in->njc;
            sparse_data->ndata = sparse_data_in->ndata;
            sparse_data->ir = (mat_uint32_t*)malloc(sparse_data->nir*sizeof(*sparse_data->ir));
            if ( NULL != sparse_data->ir )
                memcpy(sparse_data->ir,sparse_data_in->ir,
                       sparse_data->nir*sizeof(*sparse_data->ir));
            sparse_data->jc = (mat_uint32_t*)malloc(sparse_data->njc*sizeof(*sparse_data->jc));
            if ( NULL != sparse_data->jc )
                memcpy(sparse_data->jc,sparse_data_in->jc,
                       sparse_data->njc*sizeof(*sparse_data->jc));
            if ( matvar->isComplex ) {
                sparse_data->data = malloc(sizeof(mat_complex_split_t));
                if ( NULL != sparse_data->data ) {
                    mat_complex_split_t *complex_data,*complex_data_in;
                    complex_data     = (mat_complex_split_t*)sparse_data->data;
                    complex_data_in  = (mat_complex_split_t*)sparse_data_in->data;
                    complex_data->Re = malloc(sparse_data->ndata*data_size);
                    complex_data->Im = malloc(sparse_data->ndata*data_size);
                    if ( NULL != complex_data->Re )
                        memcpy(complex_data->Re,complex_data_in->Re,
                               sparse_data->ndata*data_size);
                    if ( NULL != complex_data->Im )
                        memcpy(complex_data->Im,complex_data_in->Im,
                               sparse_data->ndata*data_size);
                }
            } else {
                sparse_data->data = malloc(sparse_data->ndata*data_size);
                if ( NULL != sparse_data->data )
                    memcpy(sparse_data->data,sparse_data_in->data,
                           sparse_data->ndata*data_size);
            }
        }
        matvar->data = sparse_data;
    } else {
        if ( matvar->isComplex ) {
            matvar->data   = malloc(sizeof(mat_complex_split_t));
            if ( NULL != matvar->data && matvar->nbytes > 0 ) {
                mat_complex_split_t *complex_data    = (mat_complex_split_t*)matvar->data;
                mat_complex_split_t *complex_data_in = (mat_complex_split_t*)data;

                complex_data->Re = malloc(matvar->nbytes);
                complex_data->Im = malloc(matvar->nbytes);
                if ( NULL != complex_data->Re )
                    memcpy(complex_data->Re,complex_data_in->Re,matvar->nbytes);
                if ( NULL != complex_data->Im )
                    memcpy(complex_data->Im,complex_data_in->Im,matvar->nbytes);
            }
        } else if ( matvar->nbytes > 0 ) {
            matvar->data   = malloc(matvar->nbytes);
            if ( NULL != matvar->data )
                memcpy(matvar->data,data,matvar->nbytes);
        }
        matvar->mem_conserve = 0;
    }

    return matvar;
}


Mat_VarDelete(mat_t *mat, const char *name)
{
    int   err = 1;
    char *tmp_name;
    char temp[7] = "XXXXXX";

    if ( NULL == mat || NULL == name )
        return err;

    if ( (tmp_name = mktemp(temp)) != NULL ) {
        enum mat_ft mat_file_ver;
        mat_t *tmp;

        switch ( mat->version ) {
            case 0x0100:
                mat_file_ver = MAT_FT_MAT5;
                break;
            case 0x0200:
                mat_file_ver = MAT_FT_MAT73;
                break;
            case 0x0010:
                mat_file_ver = MAT_FT_MAT4;
                break;
            default:
                mat_file_ver = MAT_FT_DEFAULT;
                break;
        }

        tmp = Mat_CreateVer(tmp_name,mat->header,mat_file_ver);
        if ( tmp != NULL ) {
            matvar_t *matvar;
            char **dir;
            size_t n;

            Mat_Rewind(mat);
            while ( NULL != (matvar = Mat_VarReadNext(mat)) ) {
                if ( 0 != strcmp(matvar->name,name) )
                    Mat_VarWrite(tmp,matvar,matvar->compression);
                else
                    err = 0;
                Mat_VarFree(matvar);
            }
            dir = tmp->dir; /* Keep directory for later assignment */
            tmp->dir = NULL;
            n = tmp->num_datasets;
            Mat_Close(tmp);

            if ( 0 == err ) {
                char *new_name = strdup_printf("%s",mat->filename);
#if defined(MAT73) && MAT73
                if ( mat_file_ver == MAT_FT_MAT73 ) {
                    if ( mat->refs_id > -1 )
                        H5Gclose(mat->refs_id);
                    H5Fclose(*(hid_t*)mat->fp);
                    free(mat->fp);
                    mat->fp = NULL;
                }
#endif
                if ( mat->fp != NULL ) {
                    fclose((FILE*)mat->fp);
                    mat->fp = NULL;
                }

                if ( (err = mat_copy(tmp_name,new_name)) == -1 ) {
                    if ( NULL != dir ) {
                        size_t i;
                        for ( i = 0; i < n; i++ ) {
                            if ( dir[i] )
                                free(dir[i]);
                        }
                        free(dir);
                    }
                    Mat_Critical("Cannot copy file from \"%s\" to \"%s\".",
                        tmp_name, new_name);
                } else if ( (err = remove(tmp_name)) == -1 ) {
                    if ( NULL != dir ) {
                        size_t i;
                        for ( i = 0; i < n; i++ ) {
                            if ( dir[i] )
                                free(dir[i]);
                        }
                        free(dir);
                    }
                    Mat_Critical("Cannot remove file \"%s\".",tmp_name);
                } else {
                    tmp = Mat_Open(new_name,mat->mode);
                    if ( NULL != tmp ) {
                        if ( mat->header )
                            free(mat->header);
                        if ( mat->subsys_offset )
                            free(mat->subsys_offset);
                        if ( mat->filename )
                            free(mat->filename);
                        if ( mat->dir ) {
                            size_t i;
                            for ( i = 0; i < mat->num_datasets; i++ ) {
                                if ( mat->dir[i] )
                                    free(mat->dir[i]);
                            }
                            free(mat->dir);
                        }
                        memcpy(mat,tmp,sizeof(mat_t));
                        free(tmp);
                        mat->num_datasets = n;
                        mat->dir = dir;
                    } else {
                        Mat_Critical("Cannot open file \"%s\".",new_name);
                    }
                }
                free(new_name);
            } else if ( (err = remove(tmp_name)) == -1 ) {
                Mat_Critical("Cannot remove file \"%s\".",tmp_name);
            }
        }
    } else {
        Mat_Critical("Cannot create a unique file name.");
    }

    return err;
}


Mat_PrintNumber(enum matio_types type, void *data)
{
    switch ( type ) {
        case MAT_T_DOUBLE:
            printf("%g",*(double*)data);
            break;
        case MAT_T_SINGLE:
            printf("%g",*(float*)data);
            break;
#ifdef HAVE_MAT_INT64_T
        case MAT_T_INT64:
#if HAVE_INTTYPES_H
            printf("%" PRIi64,*(mat_int64_t*)data);
#elif defined(_MSC_VER) && _MSC_VER >= 1200
            printf("%I64i",*(mat_int64_t*)data);
#elif defined(HAVE_LONG_LONG_INT)
            printf("%lld",(long long)(*(mat_int64_t*)data));
#else
            printf("%ld",(long)(*(mat_int64_t*)data));
#endif
            break;
#endif
#ifdef HAVE_MAT_UINT64_T
        case MAT_T_UINT64:
#if HAVE_INTTYPES_H
            printf("%" PRIu64,*(mat_uint64_t*)data);
#elif defined(_MSC_VER) && _MSC_VER >= 1200
            printf("%I64u",*(mat_uint64_t*)data);
#elif defined(HAVE_UNSIGNED_LONG_LONG_INT)
            printf("%llu",(unsigned long long)(*(mat_uint64_t*)data));
#else
            printf("%lu",(unsigned long)(*(mat_uint64_t*)data));
#endif
            break;
#endif
        case MAT_T_INT32:
            printf("%d",*(mat_int32_t*)data);
            break;
        case MAT_T_UINT32:
            printf("%u",*(mat_uint32_t*)data);
            break;
        case MAT_T_INT16:
            printf("%hd",*(mat_int16_t*)data);
            break;
        case MAT_T_UINT16:
            printf("%hu",*(mat_uint16_t*)data);
            break;
        case MAT_T_INT8:
            printf("%hhd",*(mat_int8_t*)data);
            break;
        case MAT_T_UINT8:
            printf("%hhu",*(mat_uint8_t*)data);
            break;
        default:
            break;
    }
}


Mat_Rewind( mat_t *mat )
{
    int err = 0;

    switch ( mat->version ) {
        case MAT_FT_MAT5:
            (void)fseek((FILE*)mat->fp,128L,SEEK_SET);
            break;
        case MAT_FT_MAT73:
            mat->next_index = 0;
            break;
        case MAT_FT_MAT4:
            (void)fseek((FILE*)mat->fp,0L,SEEK_SET);
            break;
        default:
            err = -1;
            break;
    }

    return err;
}


Mat_GetLibraryVersion(int *major,int *minor,int *release)
{
    if ( NULL != major )
        *major = MATIO_MAJOR_VERSION;
    if ( NULL != minor )
        *minor = MATIO_MINOR_VERSION;
    if ( NULL != release )
        *release = MATIO_RELEASE_LEVEL;
}


static errno_t sssctl_restore(bool force_start, bool force_restart)
{
    errno_t ret;

    if (!sssctl_start_sssd(force_start)) {
        return ERR_SSSD_NOT_RUNNING;
    }

    if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
        ret = sssctl_run_command((const char *[]){"sss_override", "user-import",
                                                  SSS_BACKUP_USER_OVERRIDES, NULL});
        if (ret != EOK) {
            ERROR("Unable to import user overrides\n");
            return ret;
        }
    }

    if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
        ret = sssctl_run_command((const char *[]){"sss_override", "group-import",
                                                  SSS_BACKUP_GROUP_OVERRIDES, NULL});
        if (ret != EOK) {
            ERROR("Unable to import group overrides\n");
            return ret;
        }
    }

    sssctl_restart_sssd(force_restart);

    ret = EOK;

    return ret;
}


virSecuritySELinuxGetDOI(virSecurityManager *mgr G_GNUC_UNUSED)
{
    /*
     * Where will the DOI come from?  SELinux configuration, or qemu
     * configuration? For the moment, we'll just set it to "0".
     */
    return SECURITY_SELINUX_VOID_DOI;
}


virSecuritySELinuxSetAllLabel(virSecurityManager *mgr,
                              virDomainDef *def,
                              const char *incomingPath G_GNUC_UNUSED,
                              bool chardevStdioLogd,
                              bool migrated G_GNUC_UNUSED)
{
    size_t i;
    virSecuritySELinuxData *data = virSecurityManagerGetPrivateData(mgr);
    virSecurityLabelDef *secdef;

    struct _virSecuritySELinuxChardevCallbackData chardevData = {
        .mgr = mgr,
        .chardevStdioLogd = chardevStdioLogd
    };

    secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);

    if (!secdef || !secdef->relabel || data->skipAllLabel)
        return 0;

    for (i = 0; i < def->ndisks; i++) {
        /* XXX fixme - we need to recursively label the entire tree :-( */
        if (virDomainDiskGetType(def->disks[i]) == VIR_STORAGE_TYPE_DIR) {
            VIR_WARN("Unable to relabel directory tree %s for disk %s",
                     virDomainDiskGetSource(def->disks[i]),
                     def->disks[i]->dst);
            continue;
        }
        if (virSecuritySELinuxSetImageLabel(mgr, def, def->disks[i]->src,
                                            VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN |
                                            VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP) < 0)
            return -1;
    }
    /* XXX fixme process  def->fss if relabel == true */

    for (i = 0; i < def->nhostdevs; i++) {
        if (virSecuritySELinuxSetHostdevLabel(mgr,
                                              def,
                                              def->hostdevs[i],
                                              NULL) < 0)
            return -1;
    }

    for (i = 0; i < def->ninputs; i++) {
        if (virSecuritySELinuxSetInputLabel(mgr, def, def->inputs[i]) < 0)
            return -1;
    }

    for (i = 0; i < def->nmems; i++) {
        if (virSecuritySELinuxSetMemoryLabel(mgr, def, def->mems[i]) < 0)
            return -1;
    }

    for (i = 0; i < def->ntpms; i++) {
        if (virSecuritySELinuxSetTPMFileLabel(mgr, def, def->tpms[i]) < 0)
            return -1;
    }

    if (virDomainChrDefForeach(def,
                               true,
                               virSecuritySELinuxSetSecurityChardevCallback,
                               &chardevData) < 0)
        return -1;

    if (virDomainSmartcardDefForeach(def,
                                     true,
                                     virSecuritySELinuxSetSecuritySmartcardCallback,
                                     mgr) < 0)
        return -1;

    for (i = 0; i < def->nsysinfo; i++) {
        if (virSecuritySELinuxSetSysinfoLabel(mgr,
                                              def->sysinfo[i],
                                              data) < 0)
            return -1;
    }

    /* This is different than kernel or initrd. The nvram store
     * is really a disk, qemu can read and write to it. */
    if (def->os.loader && def->os.loader->nvram &&
        secdef && secdef->imagelabel &&
        virSecuritySELinuxSetFilecon(mgr, def->os.loader->nvram,
                                     secdef->imagelabel, true) < 0)
        return -1;

    if (def->os.kernel &&
        virSecuritySELinuxSetFilecon(mgr, def->os.kernel,
                                     data->content_context, true) < 0)
        return -1;

    if (def->os.initrd &&
        virSecuritySELinuxSetFilecon(mgr, def->os.initrd,
                                     data->content_context, true) < 0)
        return -1;

    if (def->os.dtb &&
        virSecuritySELinuxSetFilecon(mgr, def->os.dtb,
                                     data->content_context, true) < 0)
        return -1;

    if (def->os.slic_table &&
        virSecuritySELinuxSetFilecon(mgr, def->os.slic_table,
                                     data->content_context, true) < 0)
        return -1;

    return 0;
}


virSecuritySELinuxDriverOpen(virSecurityManager *mgr)
{
    return virSecuritySELinuxInitialize(mgr);
}


virSecuritySELinuxLXCInitialize(virSecurityManager *mgr)
{
    g_autoptr(virConf) selinux_conf = NULL;
    virSecuritySELinuxData *data = virSecurityManagerGetPrivateData(mgr);

    data->skipAllLabel = true;

    data->label_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0);
    if (!data->label_handle) {
        virReportSystemError(errno, "%s",
                             _("cannot open SELinux label_handle"));
        return -1;
    }

    if (!(selinux_conf = virConfReadFile(selinux_lxc_contexts_path(), 0)))
        goto error;

    if (virConfGetValueString(selinux_conf, "process", &data->domain_context) < 0)
        goto error;

    if (!data->domain_context) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("missing 'process' value in selinux lxc contexts file '%s'"),
                       selinux_lxc_contexts_path());
        goto error;
    }

    if (virConfGetValueString(selinux_conf, "file", &data->file_context) < 0)
        goto error;

    if (!data->file_context) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("missing 'file' value in selinux lxc contexts file '%s'"),
                       selinux_lxc_contexts_path());
        goto error;
    }

    if (virConfGetValueString(selinux_conf, "content", &data->content_context) < 0)
        goto error;

    if (!data->content_context) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("missing 'content' value in selinux lxc contexts file '%s'"),
                       selinux_lxc_contexts_path());
        goto error;
    }

    if (!(data->mcs = virHashNew(NULL)))
        goto error;

    return 0;

 error:
    selabel_close(data->label_handle);
    data->label_handle = NULL;
    VIR_FREE(data->domain_context);
    VIR_FREE(data->file_context);
    VIR_FREE(data->content_context);
    virHashFree(data->mcs);
    return -1;
}


virSecuritySELinuxSetFilecon(virSecurityManager *mgr,
                             const char *path,
                             const char *tcon,
                             bool remember)
{
    bool privileged = virSecurityManagerGetPrivileged(mgr);
    char *econ = NULL;
    int refcount;
    int rc;
    bool rollback = false;
    int ret = -1;

    if ((rc = virSecuritySELinuxTransactionAppend(path, tcon,
                                                  remember, false)) < 0)
        return -1;
    else if (rc > 0)
        return 0;

    if (remember) {
        if (getfilecon_raw(path, &econ) < 0 &&
            errno != ENOTSUP && errno != ENODATA) {
            virReportSystemError(errno,
                                 _("unable to get SELinux context of %s"),
                                 path);
            goto cleanup;
        }

        if (econ) {
            refcount = virSecuritySELinuxRememberLabel(path, econ);
            if (refcount > 0)
                rollback = true;
            if (refcount == -2) {
                /* Not supported. Don't error though. */
            } else if (refcount < 0) {
                goto cleanup;
            } else if (refcount > 1) {
                /* Refcount is greater than 1 which means that there
                 * is @refcount domains using the @path. Do not
                 * change the label (as it would almost certainly
                 * cause the other domains to lose access to the
                 * @path). However, the refcounter was
                 * incremented in XATTRs so decrease it. */
                if (STRNEQ(econ, tcon)) {
                    virReportError(VIR_ERR_OPERATION_INVALID,
                                   _("Setting different SELinux label on %s "
                                     "which is already in use"), path);
                    goto cleanup;
                }
            }
        }
    }

    rc = virSecuritySELinuxSetFileconImpl(path, tcon, privileged);
    if (rc < 0)
        goto cleanup;

    /* Do not try restoring the label if it was not changed
     * (setting it failed in a non-critical fashion) */
    if (rc == 0)
        rollback = false;

    ret = 0;
 cleanup:
    if (rollback) {
        virErrorPtr origerr;

        virErrorPreserveLast(&origerr);
        /* Try to restore the label. This is done so that XATTRs
         * are left in the same state as when the control entered
         * this function. However, if our attempt fails, there's
         * not much we can do. XATTRs refcounting is fubar'ed and
         * the only option we have is warn users. */
        if (virSecuritySELinuxRestoreFileLabel(mgr, path, remember) < 0)
            VIR_WARN("Unable to restore label on '%s'. "
                     "XATTRs might have been left in inconsistent state.",
                     path);

        virErrorRestore(&origerr);

    }
    freecon(econ);
    return ret;
}


virSecuritySELinuxSetFileLabels(virSecurityManager *mgr,
                                const char *path,
                                virSecurityLabelDef *seclabel)
{
    int ret = 0;
    struct dirent *ent;
    char *filename = NULL;
    g_autoptr(DIR) dir = NULL;

    if ((ret = virSecuritySELinuxSetFilecon(mgr, path, seclabel->imagelabel, true)))
        return ret;

    if (!virFileIsDir(path))
        return 0;

    if (virDirOpen(&dir, path) < 0)
        return -1;

    while ((ret = virDirRead(dir, &ent, path)) > 0) {
        filename = g_strdup_printf("%s/%s", path, ent->d_name);
        ret = virSecuritySELinuxSetFilecon(mgr, filename,
                                           seclabel->imagelabel, true);
        VIR_FREE(filename);
        if (ret < 0)
            break;
    }
    if (ret < 0)
        virReportSystemError(errno, _("Unable to label files under %s"),
                             path);

    return ret;
}


virSecuritySELinuxDriverProbe(const char *virtDriver)
{
    if (is_selinux_enabled() <= 0)
        return SECURITY_DRIVER_DISABLE;

    if (virtDriver && STREQ(virtDriver, "LXC") &&
        !virFileExists(selinux_lxc_contexts_path())) {
        return SECURITY_DRIVER_DISABLE;
    }

    return SECURITY_DRIVER_ENABLE;
}


virSecuritySELinuxMoveImageMetadataHelper(pid_t pid G_GNUC_UNUSED,
                                          void *opaque)
{
    struct virSecuritySELinuxMoveImageMetadataData *data = opaque;
    const char *paths[2] = { data->src, data->dst };
    virSecurityManagerMetadataLockState *state;
    int ret;

    if (!(state = virSecurityManagerMetadataLock(data->mgr, paths, G_N_ELEMENTS(paths))))
        return -1;

    ret = virSecurityMoveRememberedLabel(SECURITY_SELINUX_NAME, data->src, data->dst);
    virSecurityManagerMetadataUnlock(data->mgr, &state);

    if (ret == -2) {
        /* Libvirt built without XATTRS */
        ret = 0;
    }

    return ret;
}


virSecuritySELinuxSetImageLabelInternal(virSecurityManager *mgr,
                                        virDomainDef *def,
                                        virStorageSource *src,
                                        virStorageSource *parent,
                                        bool isChainTop)
{
    virSecuritySELinuxData *data = virSecurityManagerGetPrivateData(mgr);
    virSecurityLabelDef *secdef;
    virSecurityDeviceLabelDef *disk_seclabel;
    virSecurityDeviceLabelDef *parent_seclabel = NULL;
    char *use_label = NULL;
    bool remember;
    g_autofree char *vfioGroupDev = NULL;
    const char *path = src->path;
    int ret;

    if (!src->path || !virStorageSourceIsLocalStorage(src))
        return 0;

    secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
    if (!secdef || !secdef->relabel)
        return 0;

    /* We can't do restore on shared resources safely. Not even
     * with refcounting implemented in XATTRs because if there
     * was a domain running with the feature turned off the
     * refcounter in XATTRs would not reflect the actual number
     * of times the resource is in use and thus the last restore
     * on the resource (which actually restores the original
     * owner) might cut off access to the domain with the feature
     * disabled.
     * For disks, a shared resource is the whole backing chain
     * but the top layer, or read only image, or disk explicitly
     * marked as shared.
     */
    remember = isChainTop && !src->readonly && !src->shared;

    disk_seclabel = virStorageSourceGetSecurityLabelDef(src,
                                                        SECURITY_SELINUX_NAME);
    parent_seclabel = virStorageSourceGetSecurityLabelDef(parent,
                                                          SECURITY_SELINUX_NAME);

    if (disk_seclabel && (!disk_seclabel->relabel || disk_seclabel->label)) {
        if (!disk_seclabel->relabel)
            return 0;

        use_label = disk_seclabel->label;
    } else if (parent_seclabel && (!parent_seclabel->relabel || parent_seclabel->label)) {
        if (!parent_seclabel->relabel)
            return 0;

        use_label = parent_seclabel->label;
    } else if (parent == src) {
        if (src->shared) {
            use_label = data->file_context;
        } else if (src->readonly) {
            use_label = data->content_context;
        } else if (secdef->imagelabel) {
            use_label = secdef->imagelabel;
        } else {
            return 0;
        }
    } else {
        use_label = data->content_context;
    }

    /* This is not very clean. But so far we don't have NVMe
     * storage pool backend so that its chownCallback would be
     * called. And this place looks least offensive. */
    if (src->type == VIR_STORAGE_TYPE_NVME) {
        const virStorageSourceNVMeDef *nvme = src->nvme;

        if (!(vfioGroupDev = virPCIDeviceAddressGetIOMMUGroupDev(&nvme->pciAddr)))
            return -1;

        path = vfioGroupDev;
    }

    ret = virSecuritySELinuxSetFilecon(mgr, path, use_label, remember);

    if (ret == 1 && !disk_seclabel) {
        /* If we failed to set a label, but virt_use_nfs let us
         * proceed anyway, then we don't need to relabel later.  */
        disk_seclabel = virSecurityDeviceLabelDefNew(SECURITY_SELINUX_NAME);
        if (!disk_seclabel)
            return -1;
        disk_seclabel->labelskip = true;
        if (VIR_APPEND_ELEMENT(src->seclabels, src->nseclabels,
                               disk_seclabel) < 0) {
            virSecurityDeviceLabelDefFree(disk_seclabel);
            return -1;
        }
        ret = 0;
    }

    return ret;
}


virSecuritySELinuxRestoreMemoryLabel(virSecurityManager *mgr,
                                     virDomainDef *def,
                                     virDomainMemoryDef *mem)
{
    int ret = -1;
    virSecurityLabelDef *seclabel;

    switch (mem->model) {
    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
    case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
        seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
        if (!seclabel || !seclabel->relabel)
            return 0;

        ret = virSecuritySELinuxRestoreFileLabel(mgr, mem->nvdimmPath, true);
        break;

    case VIR_DOMAIN_MEMORY_MODEL_DIMM:
    case VIR_DOMAIN_MEMORY_MODEL_NONE:
    case VIR_DOMAIN_MEMORY_MODEL_LAST:
        ret = 0;
        break;
    }

    return ret;
}


virSecuritySELinuxSetTPMFileLabel(virSecurityManager *mgr,
                                  virDomainDef *def,
                                  virDomainTPMDef *tpm)
{
    int rc;
    virSecurityLabelDef *seclabel;
    char *cancel_path;
    const char *tpmdev;

    seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
    if (seclabel == NULL)
        return 0;

    switch (tpm->type) {
    case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
        tpmdev = tpm->data.passthrough.source.data.file.path;
        rc = virSecuritySELinuxSetFilecon(mgr, tpmdev, seclabel->imagelabel, false);
        if (rc < 0)
            return -1;

        if ((cancel_path = virTPMCreateCancelPath(tpmdev)) != NULL) {
            rc = virSecuritySELinuxSetFilecon(mgr,
                                              cancel_path,
                                              seclabel->imagelabel, false);
            VIR_FREE(cancel_path);
            if (rc < 0) {
                virSecuritySELinuxRestoreTPMFileLabelInt(mgr, def, tpm);
                return -1;
            }
        } else {
            return -1;
        }
        break;
    case VIR_DOMAIN_TPM_TYPE_EMULATOR:
        tpmdev = tpm->data.emulator.source.data.nix.path;
        rc = virSecuritySELinuxSetFilecon(mgr, tpmdev, seclabel->imagelabel, false);
        if (rc < 0)
            return -1;
        break;
    case VIR_DOMAIN_TPM_TYPE_LAST:
        break;
    }

    return 0;
}


virSecuritySELinuxMoveImageMetadata(virSecurityManager *mgr,
                                    pid_t pid,
                                    virStorageSource *src,
                                    virStorageSource *dst)
{
    struct virSecuritySELinuxMoveImageMetadataData data = { .mgr = mgr, 0 };
    int rc;

    if (src && virStorageSourceIsLocalStorage(src))
        data.src = src->path;

    if (dst && virStorageSourceIsLocalStorage(dst))
        data.dst = dst->path;

    if (!data.src)
        return 0;

    if (pid == -1) {
        rc = virProcessRunInFork(virSecuritySELinuxMoveImageMetadataHelper,
                                 &data);
    } else {
        rc = virProcessRunInMountNamespace(pid,
                                           virSecuritySELinuxMoveImageMetadataHelper,
                                           &data);
    }

    return rc;
}


virSecuritySELinuxSetFileconImpl(const char *path,
                                 const char *tcon,
                                 bool privileged)
{
    /* Be aware that this function might run in a separate process.
     * Therefore, any driver state changes would be thrown away. */

    VIR_INFO("Setting SELinux context on '%s' to '%s'", path, tcon);

    if (setfilecon_raw(path, (const char *)tcon) < 0) {
        int setfilecon_errno = errno;

        /* If the error complaint is related to an image hosted on a (possibly
         * read-only) NFS mount, or a usbfs/sysfs filesystem not supporting
         * labelling, then just ignore it & hope for the best.  The user
         * hopefully sets one of the necessary SELinux virt_use_{nfs,usb,pci}
         * boolean tunables to allow it ...
         */
        VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
        if (setfilecon_errno != EOPNOTSUPP && setfilecon_errno != ENOTSUP &&
            setfilecon_errno != EROFS) {
        VIR_WARNINGS_RESET
            /* However, don't claim error if SELinux is in Enforcing mode and
             * we are running as unprivileged user and we really did see EPERM.
             * Otherwise we want to return error if SELinux is Enforcing. */
            if (security_getenforce() == 1 &&
                (setfilecon_errno != EPERM || privileged)) {
                virReportSystemError(setfilecon_errno,
                                     _("unable to set security context '%s' on '%s'"),
                                     tcon, path);
                return -1;
            }
            VIR_WARN("unable to set security context '%s' on '%s' (errno %d)",
                     tcon, path, setfilecon_errno);
        } else {
            const char *msg;
            if (virFileIsSharedFSType(path, VIR_FILE_SHFS_NFS) == 1 &&
                security_get_boolean_active("virt_use_nfs") != 1) {
                msg = _("Setting security context '%s' on '%s' not supported. "
                        "Consider setting virt_use_nfs");
                if (security_getenforce() == 1)
                    VIR_WARN(msg, tcon, path);
                else
                    VIR_INFO(msg, tcon, path);
            } else {
                VIR_INFO("Setting security context '%s' on '%s' not supported",
                         tcon, path);
            }
        }

        return 1;
    }
    return 0;
}


virSecuritySELinuxDomainSetPathLabel(virSecurityManager *mgr,
                                     virDomainDef *def,
                                     const char *path,
                                     bool allowSubtree G_GNUC_UNUSED)
{
    virSecurityLabelDef *seclabel;

    seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
    if (!seclabel || !seclabel->relabel)
        return 0;

    return virSecuritySELinuxSetFilecon(mgr, path, seclabel->imagelabel, true);
}


virSecuritySELinuxSetDaemonSocketLabel(virSecurityManager *mgr G_GNUC_UNUSED,
                                       virDomainDef *def)
{
    /* TODO: verify DOI */
    virSecurityLabelDef *secdef;
    char *scon = NULL;
    char *str = NULL;
    int rc = -1;

    secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
    if (!secdef || !secdef->label)
        return 0;

    if (STRNEQ(SECURITY_SELINUX_NAME, secdef->model)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("security label driver mismatch: "
                         "'%s' model configured for domain, but "
                         "hypervisor driver is '%s'."),
                       secdef->model, SECURITY_SELINUX_NAME);
        goto done;
    }

    if (getcon_raw(&scon) == -1) {
        virReportSystemError(errno,
                             _("unable to get current process context '%s'"),
                             secdef->label);
        goto done;
    }

    if (!(str = virSecuritySELinuxContextAddRange(secdef->label, scon)))
        goto done;

    VIR_DEBUG("Setting VM %s socket context %s", def->name, str);
    if (setsockcreatecon_raw(str) == -1) {
        virReportSystemError(errno,
                             _("unable to set socket security context '%s'"), str);
        goto done;
    }

    rc = 0;
 done:

    if (security_getenforce() != 1)
        rc = 0;
    freecon(scon);
    VIR_FREE(str);
    return rc;
}


virSecuritySELinuxRestoreUSBLabel(virUSBDevice *dev G_GNUC_UNUSED,
                                  const char *file,
                                  void *opaque)
{
    virSecurityManager *mgr = opaque;

    return virSecuritySELinuxRestoreFileLabel(mgr, file, true);
}


OlmPkSigning *olm_pk_signing(void * memory) {
    olm::unset(memory, sizeof(OlmPkSigning));
    return new(memory) OlmPkSigning;
}


void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
	php_url *url;
	size_t old_len = Z_STRLEN_P(value);

	if (flags & (FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
		php_error_docref(NULL, E_DEPRECATED,
			"explicit use of FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED is deprecated");
	}

	php_filter_url(value, flags, option_array, charset);

	if (Z_TYPE_P(value) != IS_STRING || old_len != Z_STRLEN_P(value)) {
		RETURN_VALIDATION_FAILED
	}

	/* Use parse_url - if it returns false, we return NULL */
	url = php_url_parse_ex(Z_STRVAL_P(value), Z_STRLEN_P(value));

	if (url == NULL) {
		RETURN_VALIDATION_FAILED
	}

	if (url->scheme != NULL &&
		(zend_string_equals_literal_ci(url->scheme, "http") || zend_string_equals_literal_ci(url->scheme, "https"))) {
		char *e, *s, *t;
		size_t l;

		if (url->host == NULL) {
			goto bad_url;
		}

		s = ZSTR_VAL(url->host);
		l = ZSTR_LEN(url->host);
		e = s + l;
		t = e - 1;

		/* An IPv6 enclosed by square brackets is a valid hostname */
		if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2)) {
			php_url_free(url);
			return;
		}

		// Validate domain
		if (!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)) {
			php_url_free(url);
			RETURN_VALIDATION_FAILED
		}
	}

	if (
		url->scheme == NULL ||
		/* some schemas allow the host to be empty */
		(url->host == NULL && (strcmp(ZSTR_VAL(url->scheme), "mailto") && strcmp(ZSTR_VAL(url->scheme), "news") && strcmp(ZSTR_VAL(url->scheme), "file"))) ||
		((flags & FILTER_FLAG_PATH_REQUIRED) && url->path == NULL) || ((flags & FILTER_FLAG_QUERY_REQUIRED) && url->query == NULL)
	) {
bad_url:
		php_url_free(url);
		RETURN_VALIDATION_FAILED
	}

	if (url->user != NULL && !is_userinfo_valid(url->user)
		|| url->pass != NULL && !is_userinfo_valid(url->pass)
	) {
		php_url_free(url);
		RETURN_VALIDATION_FAILED

	}

	php_url_free(url);
}


static int firebird_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
{
	pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;

	if (isc_rollback_transaction(H->isc_status, &H->tr)) {
		RECORD_ERROR(dbh);
		return 0;
	}
	return 1;
}


static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */
{
	pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;

	switch (attr) {
		case PDO_ATTR_AUTOCOMMIT:
			{
				zend_bool bval = zval_get_long(val)? 1 : 0;

				/* ignore if the new value equals the old one */
				if (dbh->auto_commit ^ bval) {
					if (dbh->in_txn) {
						if (bval) {
							/* turning on auto_commit with an open transaction is illegal, because
							   we won't know what to do with it */
							H->last_app_error = "Cannot enable auto-commit while a transaction is already open";
							return 0;
						} else {
							/* close the transaction */
							if (!firebird_handle_commit(dbh)) {
								break;
							}
							dbh->in_txn = 0;
						}
					}
					dbh->auto_commit = bval;
				}
			}
			return 1;

		case PDO_ATTR_FETCH_TABLE_NAMES:
			H->fetch_table_names = zval_get_long(val)? 1 : 0;
			return 1;

		case PDO_FB_ATTR_DATE_FORMAT:
			{
				zend_string *str = zval_get_string(val);
				if (H->date_format) {
					efree(H->date_format);
				}
				spprintf(&H->date_format, 0, "%s", ZSTR_VAL(str));
				zend_string_release_ex(str, 0);
			}
			return 1;

		case PDO_FB_ATTR_TIME_FORMAT:
			{
				zend_string *str = zval_get_string(val);
				if (H->time_format) {
					efree(H->time_format);
				}
				spprintf(&H->time_format, 0, "%s", ZSTR_VAL(str));
				zend_string_release_ex(str, 0);
			}
			return 1;

		case PDO_FB_ATTR_TIMESTAMP_FORMAT:
			{
				zend_string *str = zval_get_string(val);
				if (H->timestamp_format) {
					efree(H->timestamp_format);
				}
				spprintf(&H->timestamp_format, 0, "%s", ZSTR_VAL(str));
				zend_string_release_ex(str, 0);
			}
			return 1;
	}
	return 0;
}


TEST_IMPL(idna_toascii) {
  /* Illegal inputs. */
  F("\xC0\x80\xC1\x80", UV_EINVAL);  /* Overlong UTF-8 sequence. */
  F("\xC0\x80\xC1\x80.com", UV_EINVAL);  /* Overlong UTF-8 sequence. */
  /* No conversion. */
  T("", "");
  T(".", ".");
  T(".com", ".com");
  T("example", "example");
  T("example-", "example-");
  T("straße.de", "xn--strae-oqa.de");
  /* Test cases adapted from punycode.js. Most are from RFC 3492. */
  T("foo.bar", "foo.bar");
  T("mañana.com", "xn--maana-pta.com");
  T("example.com.", "example.com.");
  T("bücher.com", "xn--bcher-kva.com");
  T("café.com", "xn--caf-dma.com");
  T("café.café.com", "xn--caf-dma.xn--caf-dma.com");
  T("☃-⌘.com", "xn----dqo34k.com");
  T("퐀☃-⌘.com", "xn----dqo34kn65z.com");
  T("💩.la", "xn--ls8h.la");
  T("mañana.com", "xn--maana-pta.com");
  T("mañana。com", "xn--maana-pta.com");
  T("mañana．com", "xn--maana-pta.com");
  T("mañana｡com", "xn--maana-pta.com");
  T("ü", "xn--tda");
  T(".ü", ".xn--tda");
  T("ü.ü", "xn--tda.xn--tda");
  T("ü.ü.", "xn--tda.xn--tda.");
  T("üëäö♥", "xn--4can8av2009b");
  T("Willst du die Blüthe des frühen, die Früchte des späteren Jahres",
    "xn--Willst du die Blthe des frhen, "
    "die Frchte des spteren Jahres-x9e96lkal");
  T("ليهمابتكلموشعربي؟", "xn--egbpdaj6bu4bxfgehfvwxn");
  T("他们为什么不说中文", "xn--ihqwcrb4cv8a8dqg056pqjye");
  T("他們爲什麽不說中文", "xn--ihqwctvzc91f659drss3x8bo0yb");
  T("Pročprostěnemluvíčesky", "xn--Proprostnemluvesky-uyb24dma41a");
  T("למההםפשוטלאמדבריםעברית", "xn--4dbcagdahymbxekheh6e0a7fei0b");
  T("यहलोगहिन्दीक्योंनहींबोलसकतेहैं",
    "xn--i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd");
  T("なぜみんな日本語を話してくれないのか",
    "xn--n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa");
  T("세계의모든사람들이한국어를이해한다면얼마나좋을까",
    "xn--989aomsvi5e83db1d2a355cv1e0vak1d"
    "wrv93d5xbh15a0dt30a5jpsd879ccm6fea98c");
  T("почемужеонинеговорятпорусски", "xn--b1abfaaepdrnnbgefbadotcwatmq2g4l");
  T("PorquénopuedensimplementehablarenEspañol",
    "xn--PorqunopuedensimplementehablarenEspaol-fmd56a");
  T("TạisaohọkhôngthểchỉnóitiếngViệt",
    "xn--TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g");
  T("3年B組金八先生", "xn--3B-ww4c5e180e575a65lsy2b");
  T("安室奈美恵-with-SUPER-MONKEYS",
    "xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n");
  T("Hello-Another-Way-それぞれの場所",
    "xn--Hello-Another-Way--fc4qua05auwb3674vfr0b");
  T("ひとつ屋根の下2", "xn--2-u9tlzr9756bt3uc0v");
  T("MajiでKoiする5秒前", "xn--MajiKoi5-783gue6qz075azm5e");
  T("パフィーdeルンバ", "xn--de-jg4avhby1noc0d");
  T("そのスピードで", "xn--d9juau41awczczp");
  T("-> $1.00 <-", "-> $1.00 <-");
  /* Test cases from https://unicode.org/reports/tr46/ */
  T("faß.de", "xn--fa-hia.de");
  T("βόλος.com", "xn--nxasmm1c.com");
  T("ශ්‍රී.com", "xn--10cl1a0b660p.com");
  T("نامه‌ای.com", "xn--mgba3gch31f060k.com");
  return 0;
}


do_replace(struct net *net, sockptr_t arg, unsigned int len)
{
	int ret;
	struct ip6t_replace tmp;
	struct xt_table_info *newinfo;
	void *loc_cpu_entry;
	struct ip6t_entry *iter;

	if (copy_from_sockptr(&tmp, arg, sizeof(tmp)) != 0)
		return -EFAULT;

	/* overflow check */
	if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
		return -ENOMEM;
	if (tmp.num_counters == 0)
		return -EINVAL;

	tmp.name[sizeof(tmp.name)-1] = 0;

	newinfo = xt_alloc_table_info(tmp.size);
	if (!newinfo)
		return -ENOMEM;

	loc_cpu_entry = newinfo->entries;
	if (copy_from_sockptr_offset(loc_cpu_entry, arg, sizeof(tmp),
			tmp.size) != 0) {
		ret = -EFAULT;
		goto free_newinfo;
	}

	ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
	if (ret != 0)
		goto free_newinfo;

	ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
			   tmp.num_counters, tmp.counters);
	if (ret)
		goto free_newinfo_untrans;
	return 0;

 free_newinfo_untrans:
	xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
		cleanup_entry(iter, net);
 free_newinfo:
	xt_free_table_info(newinfo);
	return ret;
}


int ipt_register_table(struct net *net, const struct xt_table *table,
		       const struct ipt_replace *repl,
		       const struct nf_hook_ops *ops, struct xt_table **res)
{
	int ret;
	struct xt_table_info *newinfo;
	struct xt_table_info bootstrap = {0};
	void *loc_cpu_entry;
	struct xt_table *new_table;

	newinfo = xt_alloc_table_info(repl->size);
	if (!newinfo)
		return -ENOMEM;

	loc_cpu_entry = newinfo->entries;
	memcpy(loc_cpu_entry, repl->entries, repl->size);

	ret = translate_table(net, newinfo, loc_cpu_entry, repl);
	if (ret != 0)
		goto out_free;

	new_table = xt_register_table(net, table, &bootstrap, newinfo);
	if (IS_ERR(new_table)) {
		ret = PTR_ERR(new_table);
		goto out_free;
	}

	/* set res now, will see skbs right after nf_register_net_hooks */
	WRITE_ONCE(*res, new_table);
	if (!ops)
		return 0;

	ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
	if (ret != 0) {
		__ipt_unregister_table(net, new_table);
		*res = NULL;
	}

	return ret;

out_free:
	xt_free_table_info(newinfo);
	return ret;
}


static int compat_table_info(const struct xt_table_info *info,
			     struct xt_table_info *newinfo)
{
	struct ipt_entry *iter;
	const void *loc_cpu_entry;
	int ret;

	if (!newinfo || !info)
		return -EINVAL;

	/* we dont care about newinfo->entries */
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	newinfo->initial_entries = 0;
	loc_cpu_entry = info->entries;
	ret = xt_compat_init_offsets(AF_INET, info->number);
	if (ret)
		return ret;
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		if (ret != 0)
			return ret;
	}
	return 0;
}


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

	ret = 0;
	counters = xt_counters_alloc(num_counters);
	if (!counters) {
		ret = -ENOMEM;
		goto out;
	}

	t = xt_request_find_table_lock(net, AF_INET, name);
	if (IS_ERR(t)) {
		ret = PTR_ERR(t);
		goto free_newinfo_counters_untrans;
	}

	/* You lied! */
	if (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 */
	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);

	xt_table_unlock(t);

	get_old_counters(oldinfo, counters);

	/* Decrease module usage counts and free resource */
	xt_entry_foreach(iter, oldinfo->entries, oldinfo->size)
		cleanup_entry(iter, net);

	xt_free_table_info(oldinfo);
	if (copy_to_user(counters_ptr, counters,
			 sizeof(struct xt_counters) * num_counters) != 0) {
		/* Silent error, can't fail, new table is already in place */
		net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n");
	}
	vfree(counters);
	return ret;

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


static int translate_compat_table(struct net *net,
				  struct xt_table_info **pinfo,
				  void **pentry0,
				  const struct compat_arpt_replace *compatr)
{
	unsigned int i, j;
	struct xt_table_info *newinfo, *info;
	void *pos, *entry0, *entry1;
	struct compat_arpt_entry *iter0;
	struct arpt_replace repl;
	unsigned int size;
	int ret;

	info = *pinfo;
	entry0 = *pentry0;
	size = compatr->size;
	info->number = compatr->num_entries;

	j = 0;
	xt_compat_lock(NFPROTO_ARP);
	ret = xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries);
	if (ret)
		goto out_unlock;
	/* Walk through entries, checking offsets. */
	xt_entry_foreach(iter0, entry0, compatr->size) {
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
							entry0,
							entry0 + compatr->size);
		if (ret != 0)
			goto out_unlock;
		++j;
	}

	ret = -EINVAL;
	if (j != compatr->num_entries)
		goto out_unlock;

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

	memset(newinfo->entries, 0, size);

	newinfo->number = compatr->num_entries;
	for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
		newinfo->hook_entry[i] = compatr->hook_entry[i];
		newinfo->underflow[i] = compatr->underflow[i];
	}
	entry1 = newinfo->entries;
	pos = entry1;
	size = compatr->size;
	xt_entry_foreach(iter0, entry0, compatr->size)
		compat_copy_entry_from_user(iter0, &pos, &size,
					    newinfo, entry1);

	/* all module references in entry0 are now gone */

	xt_compat_flush_offsets(NFPROTO_ARP);
	xt_compat_unlock(NFPROTO_ARP);

	memcpy(&repl, compatr, sizeof(*compatr));

	for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
		repl.hook_entry[i] = newinfo->hook_entry[i];
		repl.underflow[i] = newinfo->underflow[i];
	}

	repl.num_counters = 0;
	repl.counters = NULL;
	repl.size = newinfo->size;
	ret = translate_table(net, newinfo, entry1, &repl);
	if (ret)
		goto free_newinfo;

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

free_newinfo:
	xt_free_table_info(newinfo);
	return ret;
out_unlock:
	xt_compat_flush_offsets(NFPROTO_ARP);
	xt_compat_unlock(NFPROTO_ARP);
	xt_entry_foreach(iter0, entry0, compatr->size) {
		if (j-- == 0)
			break;
		compat_release_entry(iter0);
	}
	return ret;
}


void ipt_unregister_table(struct net *net, struct xt_table *table,
			  const struct nf_hook_ops *ops)
{
	if (ops)
		ipt_unregister_table_pre_exit(net, table, ops);
	__ipt_unregister_table(net, table);
}


void arpt_unregister_table_pre_exit(struct net *net, struct xt_table *table,
				    const struct nf_hook_ops *ops)
{
	nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
}


find_check_entry(struct arpt_entry *e, struct net *net, const char *name,
		 unsigned int size,
		 struct xt_percpu_counter_alloc_state *alloc_state)
{
	struct xt_entry_target *t;
	struct xt_target *target;
	int ret;

	if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
		return -ENOMEM;

	t = arpt_get_target(e);
	target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
					t->u.user.revision);
	if (IS_ERR(target)) {
		ret = PTR_ERR(target);
		goto out;
	}
	t->u.kernel.target = target;

	ret = check_target(e, net, name);
	if (ret)
		goto err;
	return 0;
err:
	module_put(t->u.kernel.target->me);
out:
	xt_percpu_counter_free(&e->counters);

	return ret;
}


compat_get_entries(struct compat_ipt_get_entries __user *uptr, int *len)
{
	int ret;
	struct compat_ipt_get_entries get;
	struct ipt_table *t;

	if (*len < sizeof(get)) {
		duprintf("compat_get_entries: %u < %u\n",
				*len, (unsigned int)sizeof(get));
		return -EINVAL;
	}

	if (copy_from_user(&get, uptr, sizeof(get)) != 0)
		return -EFAULT;

	if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
		duprintf("compat_get_entries: %u != %u\n", *len,
			(unsigned int)(sizeof(struct compat_ipt_get_entries) +
			get.size));
		return -EINVAL;
	}

	xt_compat_lock(AF_INET);
	t = xt_find_table_lock(AF_INET, get.name);
	if (t && !IS_ERR(t)) {
		struct xt_table_info *private = t->private;
		struct xt_table_info info;
		duprintf("t->private->number = %u\n",
			 private->number);
		ret = compat_table_info(private, &info);
		if (!ret && get.size == info.size) {
			ret = compat_copy_entries_to_user(private->size,
						   t, uptr->entrytable);
		} else if (!ret) {
			duprintf("compat_get_entries: I've got %u not %u!\n",
				 private->size,
				 get.size);
			ret = -EINVAL;
		}
		compat_flush_offsets();
		module_put(t->me);
		xt_table_unlock(t);
	} else
		ret = t ? PTR_ERR(t) : -ENOENT;

	xt_compat_unlock(AF_INET);
	return ret;
}


static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
		void * __user *dstptr, compat_uint_t *size)
{
	return xt_compat_match_to_user(m, dstptr, size);
}