/********************************************************/
/* 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_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,
false)))
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_SEC_READ_CONTROL
| NT_DIRECTORY_QUERY
| NT_DIRECTORY_TRAVERSE
| NT_DIRECTORY_CREATE_OBJECT
| NT_DIRECTORY_CREATE_SUBDIRECTORY,
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,
false)))
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 __stdcall __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_SYMBOLIC_LINK_QUERY,
&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,false);
__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);
}