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

#include <ntapi/nt_object.h>
#include <ntapi/nt_file.h>
#include <ntapi/nt_mount.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

typedef enum __dos_drive_handle_type {
	__DOS_DRIVE_DEVICE_HANDLE,
	__DOS_DRIVE_ROOT_HANDLE
} _dos_drive_handle_type;

typedef struct __dos_name_buffer {
	wchar16_t	global_prefix[4];
	wchar16_t	dos_letter;
	wchar16_t	colon;
	wchar16_t	root;
	wchar16_t	null_termination;
} _dos_name_buffer;


static int32_t __stdcall __tt_connect_to_mount_point_manager(void)
{
	int32_t			status;

	void *			hdev;
	void *			hdev_prev;
	nt_oa			oa;
	nt_iosb			iosb;
	nt_unicode_string	dev_name;
	uint16_t		dev_name_buffer[] = {
				'\\','?','?','\\',
				'M','o','u','n','t',
				'P','o','i','n','t',
				'M','a','n','a','g','e','r',0};

	dev_name.strlen = sizeof(wchar16_t) * (4+5+5+7);
	dev_name.maxlen = 0;
	dev_name.buffer = dev_name_buffer;

	oa.len		= sizeof(nt_oa);
	oa.root_dir	= (void *)0;
	oa.obj_name	= &dev_name;
	oa.obj_attr	= NT_OBJ_CASE_INSENSITIVE;
	oa.sec_desc	= (nt_sd *)0;
	oa.sec_qos	= (nt_sqos *)0;

	status = __ntapi->zw_create_file(
		&hdev,
		NT_SEC_SYNCHRONIZE | NT_FILE_READ_ATTRIBUTES,
		&oa,
		&iosb,
		0,
		NT_FILE_ATTRIBUTE_NORMAL,
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE,
		NT_FILE_OPEN,
		NT_FILE_NON_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_NONALERT,
		(void *)0,
		0);

	if (status != NT_STATUS_SUCCESS)
		return status;

	hdev_prev = (void *)at_locked_cas(
		(intptr_t *)&__ntapi_internals()->hdev_mount_point_mgr,
		0,(intptr_t)hdev);

	if (hdev_prev)
		__ntapi->zw_close(hdev);

	return status;
}


static int32_t __stdcall __tt_get_dos_drive_device_or_root_handle(
	__out	void **			hdrive,
	__in	wchar16_t *		drive_letter,
	__in	_dos_drive_handle_type	handle_type)
{
	#define		__common_mode	(NT_FILE_SYNCHRONOUS_IO_ALERT)
	#define		__common_access	(NT_SEC_SYNCHRONIZE \
					| NT_FILE_READ_ATTRIBUTES)

	int32_t			status;

	nt_oa			oa;
	nt_iosb			iosb;
	uint32_t		open_flags;
	uint32_t		access_flags;
	nt_unicode_string	dos_name;
	_dos_name_buffer	dos_name_buffer = {
					{'\\','?','?','\\'},
					'_',':',0,0};

	if (!hdrive || !drive_letter)
		return NT_STATUS_INVALID_PARAMETER;

	if ((*drive_letter>='A') && (*drive_letter<='Z'))
		dos_name_buffer.dos_letter = *drive_letter;
	else if ((*drive_letter>='a') && (*drive_letter<='z'))
		dos_name_buffer.dos_letter = *drive_letter + 'A' - 'a';
	else
		return NT_STATUS_INVALID_PARAMETER_2;

	dos_name.strlen = ((size_t)(&((_dos_name_buffer *)0)->root));
	dos_name.maxlen = 0;
	dos_name.buffer = &(dos_name_buffer.global_prefix[0]);

	switch (handle_type) {
		case __DOS_DRIVE_DEVICE_HANDLE:
			open_flags	= __common_mode;
			access_flags	= __common_access;
			break;

		case __DOS_DRIVE_ROOT_HANDLE:
			open_flags	= __common_mode   | NT_FILE_DIRECTORY_FILE;
			access_flags	= __common_access | NT_FILE_READ_ACCESS;
			dos_name_buffer.root = '\\';
			dos_name.strlen += sizeof(wchar16_t);
			break;
		default:
			open_flags	= 0;
			access_flags	= 0;
			break;
	}

	oa.len		= sizeof(nt_oa);
	oa.root_dir	= (void *)0;
	oa.obj_name	= &dos_name;
	oa.obj_attr	= NT_OBJ_INHERIT;
	oa.sec_desc	= (nt_sd *)0;
	oa.sec_qos	= (nt_sqos *)0;

	status = __ntapi->zw_open_file(
		hdrive,
		access_flags,
		&oa,
		&iosb,
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE,
		open_flags);

	return status;
}


int32_t __stdcall	__ntapi_tt_get_dos_drive_device_handle(
	__out	void **			hdevice,
	__in	wchar16_t *		drive_letter)
{
	return __tt_get_dos_drive_device_or_root_handle(
		hdevice,
		drive_letter,
		__DOS_DRIVE_DEVICE_HANDLE);
}


int32_t __stdcall	__ntapi_tt_get_dos_drive_root_handle(
	__out	void **			hroot,
	__in	wchar16_t *		drive_letter)
{
	return __tt_get_dos_drive_device_or_root_handle(
		hroot,
		drive_letter,
		__DOS_DRIVE_ROOT_HANDLE);
}



int32_t __stdcall	__ntapi_tt_get_dos_drive_device_name(
	__in	void *			hdevice		__optional,
	__in	wchar16_t *		drive_letter	__optional,
	__out	nt_mount_dev_name *	buffer,
	__in	uint32_t		buffer_size)
{
	int32_t			status;
	nt_iosb			iosb;

	if (!hdevice && (status = __tt_get_dos_drive_device_or_root_handle(
			&hdevice,
			drive_letter,
			__DOS_DRIVE_DEVICE_HANDLE)))
		return status;

	return __ntapi->zw_device_io_control_file(
		hdevice,
		(void *)0,
		(nt_io_apc_routine *)0,
		(void *)0,
		&iosb,
		NT_IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
		(void *)0,
		0,
		buffer,
		buffer_size);
}


int32_t __stdcall	__ntapi_tt_get_dos_drive_mount_points(
	__in	void *			hdevice		__optional,
	__in	wchar16_t *		drive_letter	__optional,
	__in	nt_mount_dev_name *	dev_name	__optional,
	__out	void *			buffer,
	__in	uint32_t		buffer_size)
{
	int32_t			status;
	nt_iosb			iosb;
	wchar16_t		dev_name_buffer[64];
	nt_mount_point_param *	dev_mount_point;
	nt_mount_points *	dev_mount_points;
	uintptr_t		addr;

	if (!dev_name) {
		dev_name = (nt_mount_dev_name *)&dev_name_buffer;
		if ((status = __ntapi_tt_get_dos_drive_device_name(
				hdevice,
				drive_letter,
				dev_name,
				sizeof(dev_name_buffer))))
			return status;
	}

	if (buffer_size < sizeof(nt_mount_mgr_mount_point) \
				+ sizeof(nt_mount_dev_name) \
				+ sizeof(dev_name->name_length))
		return NT_STATUS_BUFFER_TOO_SMALL;

	dev_mount_point = (nt_mount_point_param *)buffer;
	dev_mount_point->symlink_name_offset = 0;
	dev_mount_point->symlink_name_length = 0;
	dev_mount_point->unique_id_offset    = 0;
	dev_mount_point->unique_id_length    = 0;
	dev_mount_point->device_name_offset  = ((size_t)(&((nt_mount_point_param *)0)->device_name));
	dev_mount_point->device_name_length  = dev_name->name_length;
	dev_mount_point->mount_points_offset = 0;

	__ntapi->tt_memcpy_utf16(
		dev_mount_point->device_name,
		dev_name->name,
		dev_name->name_length);

	addr = (uintptr_t)(dev_mount_point->device_name) + dev_name->name_length;
	addr += sizeof(uintptr_t) - 1;
	addr /= sizeof(uintptr_t);
	addr *= sizeof(uintptr_t);
	dev_mount_points = (nt_mount_points *)addr;


	if (!__ntapi_internals()->hdev_mount_point_mgr)
		status = __tt_connect_to_mount_point_manager();

	if (!__ntapi_internals()->hdev_mount_point_mgr)
		return status;


	status = __ntapi->zw_device_io_control_file(
		__ntapi_internals()->hdev_mount_point_mgr,
		(void *)0,
		(nt_io_apc_routine *)0,
		(void *)0,
		&iosb,
		NT_IOCTL_MOUNTMGR_QUERY_POINTS,
		dev_mount_point,
		(uint32_t)(uintptr_t)&(((nt_mount_point_param *)0)->device_name) + dev_name->name_length,
		dev_mount_points,
		(uint32_t)((uintptr_t)buffer + buffer_size - addr));

	dev_mount_point->mount_points_offset = (uint16_t)((uintptr_t)addr - (uintptr_t)buffer);

	return status;
}


int32_t __stdcall	__ntapi_tt_dev_mount_points_to_statfs(
	__in		nt_mount_points *	mount_points,
	__in_out	nt_statfs *		statfs)
{
	int32_t				status;
	uint32_t			hash;
	uint32_t			i;

	nt_mount_mgr_mount_point *	mount_point;
	char *				symlink;

	mount_point = mount_points->mount_points;
	statfs->nt_drive_letter = 0;


	for (i = 0; i < mount_points->number; i++, mount_point++) {
		symlink = (char *)mount_points + mount_point->symlink_name_offset;

		/* both prefixes of interest happen to be of the same length */
		hash = __ntapi->tt_buffer_crc32(
			0, symlink, __DOS_DEVICES_PREFIX_LEN);

		if (hash == __DOS_DEVICES_PREFIX_HASH)
			statfs->nt_drive_letter = ((nt_dos_devices_name *)(symlink))->letter;
		else if (hash == __VOLUME_PATH_PREFIX_HASH) {
			status = __ntapi_tt_utf16_string_to_guid(
				(nt_guid_str_utf16 *)(symlink \
					+ __VOLUME_PATH_PREFIX_LEN \
					- sizeof(wchar16_t)),
				&statfs->nt_volume_guid);

			if (status != NT_STATUS_SUCCESS)
				return status;
		}
	}

	return 0;
}


int32_t __stdcall	__ntapi_tt_get_dos_drive_letter_from_device(
	__in	void *			hdevice		__optional,
	__out	wchar16_t *		drive_letter,
	__in	nt_mount_dev_name *	dev_name	__optional,
	__out	void *			buffer,
	__in	uint32_t		buffer_size)
{
	int32_t			status;
	wchar16_t		dev_name_buffer[128];
	nt_statfs		statfs;
	uint32_t		offset;
	nt_mount_points *	mnt_points;

	if (!dev_name) {
		dev_name = (nt_mount_dev_name *)&dev_name_buffer;
		status = __ntapi_tt_get_dos_drive_device_name(
			hdevice,
			(wchar16_t *)0,
			dev_name,
			sizeof(dev_name_buffer));

		if (status != NT_STATUS_SUCCESS)
			return status;
	}


	offset = ((nt_mount_point_param *)buffer)->mount_points_offset;
	mnt_points = (nt_mount_points *)((uintptr_t)buffer + offset);

	status = __ntapi_tt_dev_mount_points_to_statfs(
		mnt_points,
		&statfs);

	if (status != NT_STATUS_SUCCESS)
		return status;

	*drive_letter = statfs.nt_drive_letter;

	return status;
}