Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/nt_port.h>
#include <ntapi/nt_tty.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"
#include "ntapi_pty.h"

static int32_t __stdcall __ntapi_pty_open_close(
	nt_pty *	pty,
	nt_iosb *	iosb,
	int32_t		opcode)
{
	int32_t		status;
	nt_pty_fd_msg	msg;

	__ntapi->tt_aligned_block_memset(&msg,0,sizeof(msg));

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

	msg.data.fdinfo.hpty	= pty->hpty;
	msg.data.fdinfo.access	= pty->access;
	msg.data.fdinfo.flags	= pty->flags;
	msg.data.fdinfo.share	= pty->share;
	msg.data.fdinfo.options	= pty->options;

	msg.data.fdinfo.luid.high = pty->luid.high;
	msg.data.fdinfo.luid.low  = pty->luid.low;

	__ntapi_tt_guid_copy(
		&msg.data.fdinfo.guid,
		&pty->guid);

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

	pty->hpty	  = msg.data.fdinfo.hpty;
	pty->section	  = msg.data.fdinfo.section;
	pty->section_size = msg.data.fdinfo.section_size;
	pty->luid.high	  = msg.data.fdinfo.luid.high;
	pty->luid.low	  = msg.data.fdinfo.luid.low;
	iosb->status	  = msg.data.ttyinfo.status;
	iosb->info	  = 0;

	return NT_STATUS_SUCCESS;
}


static int32_t __fastcall __ntapi_pty_free(nt_pty * pty)
{
	void *	addr;
	size_t	size;

	/* unmap section */
	if (pty->section_addr)
		__ntapi->zw_unmap_view_of_section(
			NT_CURRENT_PROCESS_HANDLE,
			pty->section_addr);

	/* free control block */
	addr = pty->addr;
	size = pty->size;

	return __ntapi->zw_free_virtual_memory(
		NT_CURRENT_PROCESS_HANDLE,
		&addr,
		&size,
		NT_MEM_RELEASE);
}


static int32_t __fastcall __ntapi_pty_fail(nt_pty * pty,int32_t status)
{
	__ntapi_pty_free(pty);
	return status;
}


static int32_t __fastcall __ntapi_pty_alloc(nt_pty ** pty)
{
	int32_t		status;
	nt_pty *	ctx;
	size_t		ctx_size;

	/* allocate control block */
	ctx = 0;
	ctx_size = sizeof(nt_pty);

	if ((status = __ntapi->zw_allocate_virtual_memory(
			NT_CURRENT_PROCESS_HANDLE,
			(void **)&ctx,
			0,&ctx_size,
			NT_MEM_COMMIT,
			NT_PAGE_READWRITE)))
		return status;

	/* init control block */
	__ntapi->tt_aligned_block_memset(
		ctx,0,ctx_size);

	ctx->addr = ctx;
	ctx->size = ctx_size;

	/* atomic lock reset */
	at_store_32(&ctx->lock[__PTY_READ],0);
	at_store_32(&ctx->lock[__PTY_WRITE],0);

	*pty = ctx;
	return NT_STATUS_SUCCESS;
}

static int32_t __ntapi_pty_connect(
	void *		hport,
	nt_pty *	ctx,
	nt_iosb *	iosb)
{
	int32_t status;

	ctx->hport = hport
		? hport
		: __ntapi_internals()->hport_tty_session;

	/* request */
	iosb = iosb ? iosb : &ctx->iosb;

	if ((status = __ntapi_pty_open_close(ctx,iosb,NT_TTY_PTY_OPEN)))
		return __ntapi_pty_fail(ctx,status);

	/* map section */
	if ((status = __ntapi->zw_map_view_of_section(
			ctx->section,
			NT_CURRENT_PROCESS_HANDLE,
			&ctx->section_addr,
			0,ctx->section_size,
			0,&ctx->section_size,
			NT_VIEW_UNMAP,0,
			NT_PAGE_READWRITE)))
		ctx->section_addr = 0;

	/* resilience */
	if (!ctx->section_addr)
		if ((status = __ntapi->zw_map_view_of_section(
				ctx->section,
				NT_CURRENT_PROCESS_HANDLE,
				&ctx->section_addr,
				0,ctx->section_size,
				0,&ctx->section_size,
				NT_VIEW_UNMAP,0,
				NT_PAGE_READWRITE)))
			return __ntapi_pty_fail(ctx,status);

	/* atomic lock reset */
	at_store_32(&ctx->lock[__PTY_READ],0);
	at_store_32(&ctx->lock[__PTY_WRITE],0);

	return NT_STATUS_SUCCESS;
}


static int32_t __ntapi_pty_open_impl(
	void *			hport,
	nt_pty **		pty,
	uint32_t		desired_access,
	nt_object_attributes*	obj_attr,
	nt_iosb *		iosb,
	uint32_t		share_access,
	uint32_t		open_options,
	nt_pty *		rpty)
{
	int32_t			status;
	nt_guid			guid;
	nt_uuid_str_utf16 *	guid_str;
	nt_pty *		ctx;
	wchar16_t *		wch;

	/* validate */
	if (!obj_attr || !obj_attr->obj_name || !obj_attr->obj_name->buffer)
		return NT_STATUS_INVALID_PARAMETER;

	if (obj_attr->obj_name->strlen != __DEVICE_PATH_PREFIX_LEN + sizeof(nt_guid_str_utf16))
		return NT_STATUS_OBJECT_PATH_INVALID;

	wch = obj_attr->obj_name->buffer;

	if ((wch[0] != '\\')
			|| (wch[1] != 'D')
			|| (wch[2] != 'e')
			|| (wch[3] != 'v')
			|| (wch[4] != 'i')
			|| (wch[5] != 'c')
			|| (wch[6] != 'e')
			|| (wch[7] != '\\'))
		return NT_STATUS_OBJECT_PATH_INVALID;

	guid_str = (nt_uuid_str_utf16 *)
		((uintptr_t)obj_attr->obj_name->buffer + __DEVICE_PATH_PREFIX_LEN);

	if (__ntapi->tt_string_to_guid_utf16(guid_str,&guid))
		return NT_STATUS_OBJECT_NAME_INVALID;

	/* control block */
	if ((status = __ntapi_pty_alloc(&ctx)))
		return status;

	__ntapi_tt_guid_copy(
		&ctx->guid,
		&guid);

	ctx->access	= desired_access;
	ctx->flags	= obj_attr->obj_attr;
	ctx->share	= share_access;
	ctx->options	= open_options;

	/* pts */
	if (rpty) {
		ctx->hpty      = rpty->hpty;
		ctx->luid.high = rpty->luid.high;
		ctx->luid.low  = rpty->luid.low;
	} else {
		ctx->hpty      = obj_attr->root_dir;
	}

	if ((status = __ntapi_pty_connect(hport,ctx,iosb)))
		return status;

	*pty = ctx;

	return NT_STATUS_SUCCESS;
}

int32_t __stdcall __ntapi_pty_open(
	void *			hport,
	nt_pty **		pty,
	uint32_t		desired_access,
	nt_object_attributes*	obj_attr,
	nt_iosb *		iosb,
	uint32_t		share_access,
	uint32_t		open_options)
{
	return __ntapi_pty_open_impl(
		hport,pty,
		desired_access,
		obj_attr,iosb,
		share_access,
		open_options,
		(nt_pty *)obj_attr->root_dir);
}

int32_t __stdcall __ntapi_pty_open_pair(
	void *			hport,
	nt_pty **		pty,
	uint32_t		desired_access,
	nt_object_attributes*	obj_attr,
	nt_iosb *		iosb,
	uint32_t		share_access,
	uint32_t		open_options)
{
	return __ntapi_pty_open_impl(
		hport,pty,
		desired_access,
		obj_attr,iosb,
		share_access,
		open_options,
		0);
}

int32_t	__stdcall __ntapi_pty_inherit(
	__in	void *			hport,
	__out	nt_pty **		pty,
	__in 	nt_pty_client_info * 	client_info)
{
	int32_t 		status;
	nt_iosb 		iosb;
	nt_pty_inherit_info 	inherit;
	nt_pty *		ctx;

	if ((status = __ntapi_pty_xquery(
			hport,&iosb,
			&inherit,sizeof(inherit),
			NT_PTY_INHERIT_INFORMATION,
			client_info)))
		return status;

	/* control block */
	if ((status = __ntapi_pty_alloc(&ctx)))
		return status;

	__ntapi_tt_guid_copy(
		&ctx->guid,
		&inherit.guid);

	ctx->hpty	= inherit.hpty;
	ctx->access	= inherit.access;
	ctx->flags	= inherit.flags;
	ctx->share	= inherit.share;
	ctx->options	= inherit.options;

	ctx->luid.low 	= inherit.luid.low;
	ctx->luid.high 	= inherit.luid.high;

	if ((status = __ntapi_pty_connect(hport,ctx,&iosb)))
		return status;

	if ((status = __ntapi_pty_set(
			ctx,&iosb,
			client_info,
			sizeof(*client_info),
			NT_PTY_CLIENT_INFORMATION))) {
		__ntapi_pty_free(ctx);
		return status;
	}

	*pty = ctx;

	return NT_STATUS_SUCCESS;
}

int32_t	__stdcall __ntapi_pty_reopen(
	__in	void *			hport,
	__in	nt_pty *		pty)
{
	return __ntapi_pty_connect(hport,pty,0);
}

int32_t __stdcall __ntapi_pty_close(nt_pty * pty)
{
	if (!pty || (pty->addr != pty))
		return NT_STATUS_INVALID_PARAMETER;

	__ntapi_pty_open_close(
		pty,&pty->iosb,NT_TTY_PTY_CLOSE);

	return __ntapi_pty_free(pty);
}

int32_t	__stdcall __ntapi_pty_inherit_runtime_ctty(
	__in	void *			hport,
     __in_out	nt_runtime_data *	rtdata)
{
	int32_t				status;
        nt_pty_client_info		client_info;

	if (rtdata->stdin_type == NT_FILE_TYPE_PTY) {
		client_info.any[0] = rtdata->ptyin[0];
		client_info.any[1] = rtdata->ptyin[1];
		client_info.any[2] = rtdata->ptyin[2];
		client_info.any[3] = rtdata->ptyin[3];

		if ((status = __ntapi_pty_inherit(
				hport,
				(nt_pty **)&rtdata->hstdin,
				&client_info)))
			return status;
	}

	if (rtdata->stdout_type == NT_FILE_TYPE_PTY) {
		if ((rtdata->stdin_type == NT_FILE_TYPE_PTY)
				&& (rtdata->ptyout[0] == rtdata->ptyin[0])
				&& (rtdata->ptyout[1] == rtdata->ptyin[1])
				&& (rtdata->ptyout[2] == rtdata->ptyin[2])
				&& (rtdata->ptyout[3] == rtdata->ptyin[3])) {
			rtdata->hstdout = rtdata->hstdin;

		} else {
			client_info.any[0] = rtdata->ptyout[0];
			client_info.any[1] = rtdata->ptyout[1];
			client_info.any[2] = rtdata->ptyout[2];
			client_info.any[3] = rtdata->ptyout[3];

			if ((status = __ntapi_pty_inherit(
					hport,
					(nt_pty **)&rtdata->hstdout,
					&client_info)))
				return status;
		}
	}

	if (rtdata->stderr_type == NT_FILE_TYPE_PTY) {
		if ((rtdata->stdin_type == NT_FILE_TYPE_PTY)
				&& (rtdata->ptyerr[0] == rtdata->ptyin[0])
				&& (rtdata->ptyerr[1] == rtdata->ptyin[1])
				&& (rtdata->ptyerr[2] == rtdata->ptyin[2])
				&& (rtdata->ptyerr[3] == rtdata->ptyin[3])) {
			rtdata->hstderr = rtdata->hstdin;

		} else if ((rtdata->stdout_type == NT_FILE_TYPE_PTY)
				&& (rtdata->ptyerr[0] == rtdata->ptyout[0])
				&& (rtdata->ptyerr[1] == rtdata->ptyout[1])
				&& (rtdata->ptyerr[2] == rtdata->ptyout[2])
				&& (rtdata->ptyerr[3] == rtdata->ptyout[3])) {
			rtdata->hstderr = rtdata->hstdout;

		} else {
			client_info.any[0] = rtdata->ptyerr[0];
			client_info.any[1] = rtdata->ptyerr[1];
			client_info.any[2] = rtdata->ptyerr[2];
			client_info.any[3] = rtdata->ptyerr[3];

			if ((status = __ntapi_pty_inherit(
					hport,
					(nt_pty **)&rtdata->hstderr,
					&client_info)))
				return status;
		}
	}

	/* hctty */
	if ((rtdata->stdin_type == NT_FILE_TYPE_PTY)
			&& (rtdata->ptyctl[0] == rtdata->ptyin[0])
			&& (rtdata->ptyctl[1] == rtdata->ptyin[1])
			&& (rtdata->ptyctl[2] == rtdata->ptyin[2])
			&& (rtdata->ptyctl[3] == rtdata->ptyin[3])) {
		rtdata->hctty = rtdata->hstdin;

	} else if ((rtdata->stdout_type == NT_FILE_TYPE_PTY)
			&& (rtdata->ptyctl[0] == rtdata->ptyout[0])
			&& (rtdata->ptyctl[1] == rtdata->ptyout[1])
			&& (rtdata->ptyctl[2] == rtdata->ptyout[2])
			&& (rtdata->ptyctl[3] == rtdata->ptyout[3])) {
		rtdata->hctty = rtdata->hstdout;

	} else if ((rtdata->stderr_type == NT_FILE_TYPE_PTY)
			&& (rtdata->ptyctl[0] == rtdata->ptyerr[0])
			&& (rtdata->ptyctl[1] == rtdata->ptyerr[1])
			&& (rtdata->ptyctl[2] == rtdata->ptyerr[2])
			&& (rtdata->ptyctl[3] == rtdata->ptyerr[3])) {
		rtdata->hctty = rtdata->hstderr;

	} else {
		client_info.any[0] = rtdata->ptyctl[0];
		client_info.any[1] = rtdata->ptyctl[1];
		client_info.any[2] = rtdata->ptyctl[2];
		client_info.any[3] = rtdata->ptyctl[3];

		if ((status = __ntapi_pty_inherit(
				hport,
				(nt_pty **)&rtdata->hctty,
				&client_info)))
			rtdata->hctty = 0;
	}

	return NT_STATUS_SUCCESS;
}