Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2017  Z. Gilboa                 */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/nt_file.h>
#include <ntapi/nt_string.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/nt_port.h>
#include <ntapi/nt_ipc.h>
#include <ntapi/nt_msq.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

static const nt_guid	g_msqpid    = NT_IPC_GUID_MSQPID;
static const wchar16_t	p_msqpid[6] = NT_IPC_OBJDIR_PREFIX_MSQPID;

static int32_t __msqctl_get_service_attr(
	nt_rtdata *		rtdata,
	nt_tty_service_info *	msqctl)
{
	nt_iosb iosb;

	/* inherited runtime data? */
	if (rtdata->msqctl_keys[0]) {
		msqctl->attr.ver_major = 0;
		msqctl->attr.ver_minor = 0;
		msqctl->attr.options   = 0;
		msqctl->attr.flags     = 0;

		msqctl->attr.type    = rtdata->msqctl_type;
		msqctl->attr.subtype = rtdata->msqctl_subtype;

		msqctl->attr.keys.key[0] = rtdata->msqctl_keys[0];
		msqctl->attr.keys.key[1] = rtdata->msqctl_keys[1];
		msqctl->attr.keys.key[2] = rtdata->msqctl_keys[2];
		msqctl->attr.keys.key[3] = rtdata->msqctl_keys[3];
		msqctl->attr.keys.key[4] = rtdata->msqctl_keys[4];
		msqctl->attr.keys.key[5] = rtdata->msqctl_keys[5];

		__ntapi->tt_guid_copy(
			&msqctl->attr.guid,
			&rtdata->msqctl_guid);

		return NT_STATUS_SUCCESS;
	}

	/* obtain service info */
	return __ntapi->tty_query_information_service(
		0,&iosb,msqctl,
		&(nt_guid)NT_PORT_GUID_MSQCTL,
		0,0);
}

static int32_t __msqctl_server_connect(
	nt_rtdata *		rtdata,
	nt_tty_service_info *	msqctl)
{
	int32_t status;

	/* already cononected? */
	if (rtdata->hmsqctl)
		return NT_STATUS_SUCCESS;

	/* connect */
	if ((status = __ntapi->ipc_connect_by_attr(
			&rtdata->hmsqctl,
			&msqctl->attr)))
		return status;

	/* update */
	rtdata->msqctl_type    = msqctl->attr.type;
	rtdata->msqctl_subtype = msqctl->attr.subtype;

	rtdata->msqctl_keys[0] = msqctl->attr.keys.key[0];
	rtdata->msqctl_keys[1] = msqctl->attr.keys.key[1];
	rtdata->msqctl_keys[2] = msqctl->attr.keys.key[2];
	rtdata->msqctl_keys[3] = msqctl->attr.keys.key[3];
	rtdata->msqctl_keys[4] = msqctl->attr.keys.key[4];
	rtdata->msqctl_keys[5] = msqctl->attr.keys.key[5];

	__ntapi->tt_guid_copy(
		&rtdata->msqctl_guid,
		&msqctl->attr.guid);

	return NT_STATUS_SUCCESS;
}

static int32_t __msqpid_symlink_set(
	nt_rtdata *		rtdata,
	nt_tty_service_info *	msqctl)
{
	int32_t			status;
	void *			hpiddir;
	nt_port_name		svcname;
	nt_unicode_string	str;

	if (rtdata->hmsqpid)
		return NT_STATUS_SUCCESS;

	if (!rtdata->hmsqpiddir) {
		if ((status = __ntapi->tt_open_ipc_object_directory(
				&hpiddir,
				NT_DIRECTORY_ALL_ACCESS,
				p_msqpid,&g_msqpid)))
			return status;

		if (at_locked_cas((intptr_t *)&rtdata->hmsqpiddir,0,(intptr_t)hpiddir))
			__ntapi->zw_close(hpiddir);
	}

	__ntapi->tt_port_name_from_attr(
		&svcname,&msqctl->attr);

	str.strlen = (uint16_t)(__offsetof(nt_port_name,null_termination));
	str.maxlen = sizeof(nt_port_name);
	str.buffer = svcname.base_named_objects;

	return __ntapi->tt_create_ipc_object_directory_entry(
		&rtdata->hmsqpid,
		NT_SEC_STANDARD_RIGHTS_ALL,
		rtdata->hmsqpiddir,
		0,&str,
		pe_get_current_process_id());

}

static int32_t	__stdcall __msq_open(
	void *			hipc,
	nt_msq_info *		msq,
	uint32_t		access,
	nt_object_attributes *	oa,
	nt_iosb *		iosb,
	uint32_t		share,
	uint32_t		msqslots,
	uint32_t		key,
	uint32_t		id,
	uint32_t		opcode)
{
	int32_t			status;
	nt_tty_port_msg		msg;
	nt_iosb			siosb;
	nt_tty_service_info	msqctl;
	nt_runtime_data *	rtdata;

	/* init */
	rtdata = (__ntapi_internals())->rtdata;

	/* msqctl service attributes */
	if (!rtdata->hmsqpid)
		if ((status = __msqctl_get_service_attr(rtdata,&msqctl)))
			return status;

	/* msqctl server */
	if ((status = __msqctl_server_connect(rtdata,&msqctl)))
		return status;

	/* msqpid symlink */
	if ((status = __msqpid_symlink_set(rtdata,&msqctl)))
		return status;

	/* hipc */
	if (!hipc && (opcode == NT_TTY_MSQ_ALLOC))
		hipc = (__ntapi_internals())->rtdata->hmsqctl;

	/* obtain msq info */
	__ntapi->tt_aligned_block_memset(
		&msg,0,sizeof(msg));

	if (!iosb)
		iosb = &siosb;

	msg.header.msg_type		= NT_LPC_NEW_MESSAGE;
	msg.header.data_size		= sizeof(nt_msq_info_msg) - sizeof(msg.header);
	msg.header.msg_size		= sizeof(msg);
	msg.ttyinfo.opcode		= opcode;

	msg.msqinfo.msqkey		= (int32_t)key;
	msg.msqinfo.msqid		= (int32_t)id;
	msg.msqinfo.msqslots		= msqslots;

	msg.msqinfo.ntattr		= oa->obj_attr;
	msg.msqinfo.ntaccess		= access;
	msg.msqinfo.ntshare		= share;

	if ((status = __ntapi->zw_request_wait_reply_port(hipc,&msg,&msg)))
		return status;
	else if (msg.ttyinfo.status)
		return msg.ttyinfo.status;

	iosb->info   = sizeof(msg.svcinfo);
	iosb->status = NT_STATUS_SUCCESS;

	/* new msqueue? */
	if (opcode == NT_TTY_MSQ_ALLOC)
		if ((status = __ntapi->ipc_connect_by_attr(
				&hipc,&msg.svcinfo.attr)))
			return status;

	/* all done */
	__ntapi->tt_aligned_block_memset(
		(uintptr_t *)msq,
		0,sizeof(*msq));

	msq->msqkey = msg.svcinfo.key;
	msq->msqid  = msg.svcinfo.id;

	msq->hport = hipc;

	return NT_STATUS_SUCCESS;
}


int32_t	__ntapi_msq_create(
	__in	void *			hport,
	__out	nt_msq_info *		msq,
	__in	uint32_t		access,
	__in	nt_object_attributes *	oa,
	__out	nt_iosb *		iosb,
	__in	uint32_t		share,
	__in	uint32_t		nslots)
{
	uint32_t key;

	/* validate */
	if (!oa->root_dir)
		return NT_STATUS_DIRECTORY_SERVICE_REQUIRED;

	if (oa->obj_name && !oa->obj_name->strlen) {
		key = 0;

	} else if (oa->obj_name) {
		if (oa->obj_name->strlen != 8 * sizeof(wchar16_t))
			return NT_STATUS_OBJECT_NAME_INVALID;

		if (__ntapi->tt_hex_utf16_to_uint32(oa->obj_name->buffer,&key))
			return NT_STATUS_OBJECT_NAME_INVALID;

	} else {
		key = 0;
	}

	/* open msqueue */
	return __msq_open(
		hport,msq,access,
		oa,iosb,share,nslots,
		key,0,NT_TTY_MSQ_ALLOC);
}


int32_t	__stdcall __ntapi_msq_open(
	__in	void *			hport,
	__out	nt_msq_info *		msq,
	__in	uint32_t		access,
	__in	nt_object_attributes *	oa,
	__out	nt_iosb *		iosb,
	__in	uint32_t		share,
	__in	uint32_t		nslots)
{
	int32_t			status;
	uint32_t		key;
	uint32_t		id;
	void *			hsymlink;
	nt_oa			ipcoa;
	void *			hipc;
	nt_rtdata *		rtdata;
	nt_tty_service_info	msqctl;

	/* init */
	rtdata = (__ntapi_internals())->rtdata;

	/* validate */
	if (!oa->root_dir)
		return NT_STATUS_DIRECTORY_SERVICE_REQUIRED;

	if (!oa->obj_name)
		return NT_STATUS_INVALID_PARAMETER;

	if (oa->obj_name->strlen != 8 * sizeof(wchar16_t))
		return NT_STATUS_OBJECT_NAME_INVALID;

	if (__ntapi->tt_hex_utf16_to_uint32(oa->obj_name->buffer,&key))
		return NT_STATUS_OBJECT_NAME_INVALID;

	/* open symlink */
	ipcoa.len      = sizeof(ipcoa);
	ipcoa.root_dir = oa->root_dir;
	ipcoa.obj_name = oa->obj_name;
	ipcoa.obj_attr = 0;
	ipcoa.sec_desc = oa->sec_desc;
	ipcoa.sec_qos  = oa->sec_qos;

	status = __ntapi->zw_open_symbolic_link_object(
		&hsymlink,
		NT_SEC_STANDARD_RIGHTS_READ | NT_GENERIC_READ,
		&ipcoa);

	switch (status) {
		case NT_STATUS_SUCCESS:
			break;

		case NT_STATUS_OBJECT_NAME_NOT_FOUND:
		case NT_STATUS_OBJECT_PATH_NOT_FOUND:
			if (oa->obj_attr & NT_OBJ_OPENIF)
				return __msq_open(
					hport,msq,access,
					oa,iosb,share,nslots,
					key,0,NT_TTY_MSQ_ALLOC);
			else
				return status;

		default:
			return status;
	}

	/* msqctl service attributes */
	if (!rtdata->hmsqpid)
		if ((status = __msqctl_get_service_attr(rtdata,&msqctl)))
			return status;

	/* msqctl server */
	if ((status = __msqctl_server_connect(rtdata,&msqctl)))
		return status;

	/* msqpid symlink */
	if ((status = __msqpid_symlink_set(rtdata,&msqctl)))
		return status;

	/* ipc connect */
	status = __ntapi->ipc_connect_by_symlink(
		&hipc,hsymlink);

	__ntapi->zw_close(
		hsymlink);

	if (status)
		return status;

	/* open by id? */
	if (oa->obj_attr & NT_OBJ_OPENLINK) {
		id  = key;
		key = 0;
	} else {
		id  = 0;
	}

	return __msq_open(
		hipc,msq,access,
		oa,iosb,share,nslots,
		key,id,NT_TTY_MSQ_OPEN);
}