You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1115 lines
34 KiB
1115 lines
34 KiB
// from base\ntdll\curdir.c
|
|
// belongs in base\ntos\rtl\path.c
|
|
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
path.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Jay Krell (JayKrell)
|
|
|
|
Revision History:
|
|
|
|
Environment:
|
|
|
|
ntdll.dll and setupdd.sys, not ntoskrnl.exe
|
|
|
|
--*/
|
|
|
|
#include "spprecmp.h"
|
|
|
|
#pragma warning(disable:4201) // nameless struct/union
|
|
|
|
#define _NTOS_ /* prevent #including ntos.h, only use functions exports from ntdll/ntoskrnl */
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "ntrtlp.h"
|
|
|
|
#define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
|
|
|
|
extern const UNICODE_STRING RtlpEmptyString = RTL_CONSTANT_STRING(L"");
|
|
extern const UNICODE_STRING RtlpSlashSlashDot = RTL_CONSTANT_STRING( L"\\\\.\\" );
|
|
extern const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING( L"\\??\\" );
|
|
|
|
//
|
|
// \\? is referred to as the "Win32Nt" prefix or root.
|
|
// Paths that start with \\? are referred to as "Win32Nt" paths.
|
|
// Fudging the \\? to \?? converts the path to an Nt path.
|
|
//
|
|
extern const UNICODE_STRING RtlpWin32NtRoot = RTL_CONSTANT_STRING( L"\\\\?" );
|
|
extern const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\" );
|
|
extern const UNICODE_STRING RtlpWin32NtUncRoot = RTL_CONSTANT_STRING( L"\\\\?\\UNC" );
|
|
extern const UNICODE_STRING RtlpWin32NtUncRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\UNC\\" );
|
|
|
|
#define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
|
|
|| (x) == STATUS_OBJECT_NAME_NOT_FOUND \
|
|
) \
|
|
? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
|
|
|
|
RTL_PATH_TYPE
|
|
NTAPI
|
|
RtlDetermineDosPathNameType_Ustr(
|
|
IN PCUNICODE_STRING String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function examines the Dos format file name and determines the
|
|
type of file name (i.e. UNC, DriveAbsolute, Current Directory
|
|
rooted, or Relative.)
|
|
|
|
Arguments:
|
|
|
|
DosFileName - Supplies the Dos format file name whose type is to be
|
|
determined.
|
|
|
|
Return Value:
|
|
|
|
RtlPathTypeUnknown - The path type can not be determined
|
|
|
|
RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
|
|
in the format \\server-name\sharename\rest-of-path
|
|
|
|
RtlPathTypeLocalDevice - The path specifies a local device in the format
|
|
\\.\rest-of-path or \\?\rest-of-path. This can be used for any device
|
|
where the nt and Win32 names are the same. For example mailslots.
|
|
|
|
RtlPathTypeRootLocalDevice - The path specifies the root of the local
|
|
devices in the format \\. or \\?
|
|
|
|
RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
|
|
path in the form drive:\rest-of-path
|
|
|
|
RtlPathTypeDriveRelative - The path specifies a drive letter relative
|
|
path in the form drive:rest-of-path
|
|
|
|
RtlPathTypeRooted - The path is rooted relative to the current disk
|
|
designator (either Unc disk, or drive). The form is \rest-of-path.
|
|
|
|
RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_PATH_TYPE ReturnValue;
|
|
const PCWSTR DosFileName = String->Buffer;
|
|
|
|
#define ENOUGH_CHARS(_cch) (String->Length >= ((_cch) * sizeof(WCHAR)))
|
|
|
|
if ( ENOUGH_CHARS(1) && IS_PATH_SEPARATOR_U(*DosFileName) ) {
|
|
if ( ENOUGH_CHARS(2) && IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
|
|
if ( ENOUGH_CHARS(3) && (DosFileName[2] == '.' ||
|
|
DosFileName[2] == '?') ) {
|
|
|
|
if ( ENOUGH_CHARS(4) && IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
|
|
// "\\.\" or "\\?\"
|
|
ReturnValue = RtlPathTypeLocalDevice;
|
|
}
|
|
else if ( String->Length == (3 * sizeof(WCHAR)) ){
|
|
// "\\." or \\?"
|
|
ReturnValue = RtlPathTypeRootLocalDevice;
|
|
}
|
|
else {
|
|
// "\\.x" or "\\?x"
|
|
ReturnValue = RtlPathTypeUncAbsolute;
|
|
}
|
|
}
|
|
else {
|
|
// "\\x"
|
|
ReturnValue = RtlPathTypeUncAbsolute;
|
|
}
|
|
}
|
|
else {
|
|
// "\x"
|
|
ReturnValue = RtlPathTypeRooted;
|
|
}
|
|
}
|
|
//
|
|
// the "*DosFileName" is left over from the PCWSTR version
|
|
// Win32 and DOS don't allow embedded nuls and much code limits
|
|
// drive letters to strictly 7bit a-zA-Z so it's ok.
|
|
//
|
|
else if (ENOUGH_CHARS(2) && *DosFileName && *(DosFileName+1)==L':') {
|
|
if (ENOUGH_CHARS(3) && IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
|
|
// "x:\"
|
|
ReturnValue = RtlPathTypeDriveAbsolute;
|
|
}
|
|
else {
|
|
// "c:x"
|
|
ReturnValue = RtlPathTypeDriveRelative;
|
|
}
|
|
}
|
|
else {
|
|
// "x", first char is not a slash / second char is not colon
|
|
ReturnValue = RtlPathTypeRelative;
|
|
}
|
|
return ReturnValue;
|
|
|
|
#undef ENOUGH_CHARS
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpDetermineDosPathNameTypeEx(
|
|
IN ULONG InFlags,
|
|
IN PCUNICODE_STRING DosPath,
|
|
OUT RTL_PATH_TYPE* OutType,
|
|
OUT ULONG* OutFlags
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RTL_PATH_TYPE PathType = 0;
|
|
BOOLEAN Win32Nt = FALSE;
|
|
BOOLEAN Win32NtUncAbsolute = FALSE;
|
|
BOOLEAN Win32NtDriveAbsolute = FALSE;
|
|
BOOLEAN IncompleteRoot = FALSE;
|
|
RTL_PATH_TYPE PathTypeAfterWin32Nt = 0;
|
|
|
|
if (OutType != NULL
|
|
) {
|
|
*OutType = RtlPathTypeUnknown;
|
|
}
|
|
if (OutFlags != NULL
|
|
) {
|
|
*OutFlags = 0;
|
|
}
|
|
if (
|
|
!RTL_SOFT_VERIFY(DosPath != NULL)
|
|
|| !RTL_SOFT_VERIFY(OutType != NULL)
|
|
|| !RTL_SOFT_VERIFY(OutFlags != NULL)
|
|
|| !RTL_SOFT_VERIFY(
|
|
(InFlags & ~(RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_OLD | RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT))
|
|
== 0)
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
PathType = RtlDetermineDosPathNameType_Ustr(DosPath);
|
|
*OutType = PathType;
|
|
if (InFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_OLD)
|
|
goto Exit;
|
|
|
|
if (DosPath->Length == sizeof(L"\\\\") - sizeof(DosPath->Buffer[0])
|
|
) {
|
|
IncompleteRoot = TRUE;
|
|
}
|
|
else if (RtlEqualUnicodeString(&RtlpWin32NtRoot, DosPath, TRUE)
|
|
) {
|
|
IncompleteRoot = TRUE;
|
|
Win32Nt = TRUE;
|
|
}
|
|
else if (RtlEqualUnicodeString(&RtlpWin32NtRootSlash, DosPath, TRUE)
|
|
) {
|
|
IncompleteRoot = TRUE;
|
|
Win32Nt = TRUE;
|
|
}
|
|
else if (RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, DosPath, TRUE)
|
|
) {
|
|
Win32Nt = TRUE;
|
|
}
|
|
|
|
if (Win32Nt) {
|
|
if (RtlEqualUnicodeString(&RtlpWin32NtUncRoot, DosPath, TRUE)
|
|
) {
|
|
IncompleteRoot = TRUE;
|
|
Win32NtUncAbsolute = TRUE;
|
|
}
|
|
else if (RtlEqualUnicodeString(&RtlpWin32NtUncRootSlash, DosPath, TRUE)
|
|
) {
|
|
IncompleteRoot = TRUE;
|
|
Win32NtUncAbsolute = TRUE;
|
|
}
|
|
else if (RtlPrefixUnicodeString(&RtlpWin32NtUncRootSlash, DosPath, TRUE)
|
|
) {
|
|
Win32NtUncAbsolute = TRUE;
|
|
}
|
|
if (Win32NtUncAbsolute
|
|
) {
|
|
Win32NtDriveAbsolute = FALSE;
|
|
} else if (!IncompleteRoot) {
|
|
const RTL_STRING_LENGTH_TYPE i = RtlpWin32NtRootSlash.Length;
|
|
UNICODE_STRING PathAfterWin32Nt = *DosPath;
|
|
|
|
PathAfterWin32Nt.Buffer += i / sizeof(PathAfterWin32Nt.Buffer[0]);
|
|
PathAfterWin32Nt.Length = PathAfterWin32Nt.Length - i;
|
|
PathAfterWin32Nt.MaximumLength = PathAfterWin32Nt.MaximumLength - i;
|
|
|
|
PathTypeAfterWin32Nt = RtlDetermineDosPathNameType_Ustr(&PathAfterWin32Nt);
|
|
if (PathTypeAfterWin32Nt == RtlPathTypeDriveAbsolute) {
|
|
Win32NtDriveAbsolute = TRUE;
|
|
}
|
|
else {
|
|
Win32NtDriveAbsolute = FALSE;
|
|
}
|
|
|
|
if (InFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT
|
|
) {
|
|
if (!RTL_SOFT_VERIFY(Win32NtDriveAbsolute
|
|
)) {
|
|
*OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INVALID;
|
|
// we still succeed the function call
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(RTLP_IMPLIES(Win32NtDriveAbsolute, Win32Nt));
|
|
ASSERT(RTLP_IMPLIES(Win32NtUncAbsolute, Win32Nt));
|
|
ASSERT(!(Win32NtUncAbsolute && Win32NtDriveAbsolute));
|
|
|
|
if (IncompleteRoot)
|
|
*OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INCOMPLETE_ROOT;
|
|
if (Win32Nt)
|
|
*OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT;
|
|
if (Win32NtUncAbsolute)
|
|
*OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT_UNC_ABSOLUTE;
|
|
if (Win32NtDriveAbsolute)
|
|
*OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT_DRIVE_ABSOLUTE;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
#define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT (0x00000001)
|
|
#define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS (0x00000002)
|
|
#define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT (0x00000003)
|
|
#define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS (0x00000004)
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpGetLengthWithoutLastPathElement(
|
|
IN ULONG Flags,
|
|
IN ULONG PathType,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* LengthOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report how long Path would be if you remove its last element.
|
|
This is much simpler than RtlRemoveLastDosPathElement.
|
|
It is used to implement the other RtlRemoveLast*PathElement.
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future expansion
|
|
Path - the path is is an NT path or a full DOS path; the various relative DOS
|
|
path types do not work, see RtlRemoveLastDosPathElement for them.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the usual hunky-dory
|
|
STATUS_NO_MEMORY - the usual stress
|
|
STATUS_INVALID_PARAMETER - the usual bug
|
|
|
|
--*/
|
|
{
|
|
ULONG Length = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RTL_PATH_TYPE DosPathType = RtlPathTypeUnknown;
|
|
ULONG DosPathFlags = 0;
|
|
ULONG AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted)
|
|
| (1UL << RtlPathTypeUncAbsolute)
|
|
| (1UL << RtlPathTypeDriveAbsolute)
|
|
| (1UL << RtlPathTypeLocalDevice) // "\\?\"
|
|
| (1UL << RtlPathTypeRootLocalDevice) // "\\?"
|
|
;
|
|
WCHAR PathSeperators[2] = { '/', '\\' };
|
|
|
|
#define LOCAL_IS_PATH_SEPARATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
|
|
|
|
if (LengthOut != NULL) {
|
|
*LengthOut = 0;
|
|
}
|
|
|
|
if ( !RTL_SOFT_VERIFY(Path != NULL)
|
|
|| !RTL_SOFT_VERIFY(Flags == 0)
|
|
|| !RTL_SOFT_VERIFY(LengthOut != NULL)
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Length = RTL_STRING_GET_LENGTH_CHARS(Path);
|
|
|
|
switch (PathType)
|
|
{
|
|
default:
|
|
case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT:
|
|
//
|
|
// RtlpDetermineDosPathNameTypeEx calls it "rooted"
|
|
// only backslashes are seperators
|
|
// path must start with backslash
|
|
// second char must not be backslash
|
|
//
|
|
AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted);
|
|
PathSeperators[0] = '\\';
|
|
if (Length > 0 && Path->Buffer[0] != '\\'
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
if (Length > 1 && Path->Buffer[1] == '\\'
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS:
|
|
AllowedDosPathTypeBits &= ~(1UL << RtlPathTypeRooted);
|
|
break;
|
|
case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT:
|
|
break;
|
|
}
|
|
|
|
if (Length == 0) {
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlpDetermineDosPathNameTypeEx(
|
|
RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT,
|
|
Path,
|
|
&DosPathType,
|
|
&DosPathFlags
|
|
);
|
|
|
|
if (!RTL_SOFT_VERIFY(NT_SUCCESS(Status))) {
|
|
goto Exit;
|
|
}
|
|
if (!RTL_SOFT_VERIFY((1UL << DosPathType) & AllowedDosPathTypeBits)
|
|
) {
|
|
//KdPrintEx();
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!RTL_SOFT_VERIFY(
|
|
(DosPathFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INVALID) == 0
|
|
)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
// skip one or more trailing path seperators
|
|
for ( ; Length != 0 && LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
|
|
// nothing
|
|
}
|
|
// skip trailing path element
|
|
for ( ; Length != 0 && !LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
|
|
// nothing
|
|
}
|
|
// skip one or more in between path seperators
|
|
for ( ; Length != 0 && LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
|
|
// nothing
|
|
}
|
|
// put back a trailing path seperator, for the sake of c:\ vs. c:
|
|
if (Length != 0) {
|
|
++Length;
|
|
}
|
|
|
|
//
|
|
// Should optionally check for "bad dos roots" here.
|
|
//
|
|
|
|
*LengthOut = Length;
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
#undef LOCAL_IS_PATH_SEPARATOR
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetLengthWithoutLastNtPathElement(
|
|
IN ULONG Flags,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* LengthOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report how long Path would be if you remove its last element.
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future expansion
|
|
Path - the path is is an NT path; the various DOS path types
|
|
do not work, see RtlRemoveLastDosPathElement for them.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the usual hunky-dory
|
|
STATUS_NO_MEMORY - the usual stress
|
|
STATUS_INVALID_PARAMETER - the usual bug
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT, Path, LengthOut);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetLengthWithoutLastFullDosOrNtPathElement(
|
|
IN ULONG Flags,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* LengthOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report how long Path would be if you remove its last element.
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future expansion
|
|
Path - the path is is an NT path; the various DOS path types
|
|
do not work, see RtlRemoveLastDosPathElement for them.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the usual hunky-dory
|
|
STATUS_NO_MEMORY - the usual stress
|
|
STATUS_INVALID_PARAMETER - the usual bug
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT, Path, LengthOut);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpCheckForBadDosRootPath(
|
|
IN ULONG Flags,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* RootType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future binary compatible expansion
|
|
|
|
Path - the path to be checked
|
|
|
|
RootType - fairly specifically what the string is
|
|
RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX - \\? or \\?\
|
|
RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX - \\?\unc or \\?\unc\
|
|
RTLP_BAD_DOS_ROOT_PATH_NT_PATH - \??\ but this i only a rough check
|
|
RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE - \\machine or \\?\unc\machine
|
|
RTLP_GOOD_DOS_ROOT_PATH - none of the above, seems ok
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
Path is NULL
|
|
or Flags uses undefined values
|
|
--*/
|
|
{
|
|
ULONG Length = 0;
|
|
ULONG Index = 0;
|
|
BOOLEAN Unc = FALSE;
|
|
BOOLEAN Unc1 = FALSE;
|
|
BOOLEAN Unc2 = FALSE;
|
|
ULONG PiecesSeen = 0;
|
|
|
|
if (RootType != NULL) {
|
|
*RootType = 0;
|
|
}
|
|
|
|
if (!RTL_SOFT_VERIFY(Path != NULL) ||
|
|
!RTL_SOFT_VERIFY(RootType != NULL) ||
|
|
!RTL_SOFT_VERIFY(Flags == 0)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Length = Path->Length / sizeof(Path->Buffer[0]);
|
|
|
|
if (Length < 3 || !RTL_IS_PATH_SEPARATOR(Path->Buffer[0])) {
|
|
*RootType = RTLP_GOOD_DOS_ROOT_PATH;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// prefix \??\ (heuristic, doesn't catch many NT paths)
|
|
if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), RTL_CONST_CAST(PUNICODE_STRING)(Path), TRUE)) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_NT_PATH;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!RTL_IS_PATH_SEPARATOR(Path->Buffer[1])) {
|
|
*RootType = RTLP_GOOD_DOS_ROOT_PATH;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// == \\?
|
|
if (RtlEqualUnicodeString(Path, &RtlpWin32NtRoot, TRUE)) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if (RtlEqualUnicodeString(Path, &RtlpWin32NtRootSlash, TRUE)) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// == \\?\unc
|
|
if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRoot, TRUE)) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRootSlash, TRUE)) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// prefix \\ or \\?\unc
|
|
// must check the longer string first, or avoid the short circuit (| instead of ||)
|
|
Unc1 = RtlPrefixUnicodeString(&RtlpWin32NtUncRootSlash, Path, TRUE);
|
|
|
|
if (RTL_IS_PATH_SEPARATOR(Path->Buffer[1])) {
|
|
Unc2 = TRUE;
|
|
}
|
|
else {
|
|
Unc2 = FALSE;
|
|
}
|
|
|
|
Unc = Unc1 || Unc2;
|
|
|
|
if (!Unc) {
|
|
*RootType = RTLP_GOOD_DOS_ROOT_PATH;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// it's unc, see if it is only a machine (note that it'd be really nice if FindFirstFile(\\machine\*)
|
|
// just worked and we didn't have to care..)
|
|
//
|
|
|
|
// point index at a slash that precedes the machine, anywhere in the run of slashes,
|
|
// but after the \\? stuff
|
|
if (Unc1) {
|
|
Index = (RtlpWin32NtUncRootSlash.Length / sizeof(RtlpWin32NtUncRootSlash.Buffer[0])) - 1;
|
|
} else {
|
|
ASSERT(Unc2);
|
|
Index = 1;
|
|
}
|
|
ASSERT(RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]));
|
|
Length = Path->Length/ sizeof(Path->Buffer[0]);
|
|
|
|
//
|
|
// skip leading slashes
|
|
//
|
|
for ( ; Index < Length && RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
|
|
PiecesSeen |= 1;
|
|
}
|
|
// skip the machine name
|
|
for ( ; Index < Length && !RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
|
|
PiecesSeen |= 2;
|
|
}
|
|
// skip the slashes between machine and share
|
|
for ( ; Index < Length && RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
|
|
PiecesSeen |= 4;
|
|
}
|
|
|
|
//
|
|
// Skip the share (make sure it's at least one char).
|
|
//
|
|
|
|
if (Index < Length && !RTL_IS_PATH_SEPARATOR(Path->Buffer[Index])) {
|
|
PiecesSeen |= 8;
|
|
}
|
|
|
|
if (PiecesSeen != 0xF) {
|
|
*RootType = RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpBadDosRootPathToEmptyString(
|
|
IN ULONG Flags,
|
|
IN OUT PUNICODE_STRING Path
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future binary compatible expansion
|
|
|
|
Path - the path to be checked and possibly emptied
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
Path is NULL
|
|
or Flags uses undefined values
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG RootType = 0;
|
|
|
|
UNREFERENCED_PARAMETER (Flags);
|
|
|
|
Status = RtlpCheckForBadDosRootPath(0, Path, &RootType);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// this is not invalid parameter, our contract is we
|
|
// go \\machine\share to empty \\?\c: to empty, etc.
|
|
//
|
|
|
|
if (RootType != RTLP_GOOD_DOS_ROOT_PATH) {
|
|
if (RootType == RTLP_BAD_DOS_ROOT_PATH_NT_PATH) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
Path->Length = 0;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetLengthWithoutLastFullDosPathElement(
|
|
IN ULONG Flags,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* LengthOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a fulldospath, like c:\, \\machine\share, \\?\unc\machine\share, \\?\c:,
|
|
return (in an out parameter) the length if the last element was cut off.
|
|
|
|
Arguments:
|
|
|
|
Flags - room for future binary compatible expansion
|
|
|
|
Path - the path to be truncating
|
|
|
|
LengthOut - the length if the last path element is removed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
Path is NULL
|
|
or LengthOut is NULL
|
|
or Flags uses undefined values
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING CheckRootString = { 0 };
|
|
|
|
//
|
|
// parameter validation is done in RtlpGetLengthWithoutLastPathElement
|
|
//
|
|
|
|
Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS, Path, LengthOut);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
goto Exit;
|
|
}
|
|
|
|
CheckRootString.Buffer = Path->Buffer;
|
|
CheckRootString.Length = (USHORT)(*LengthOut * sizeof(*Path->Buffer));
|
|
CheckRootString.MaximumLength = CheckRootString.Length;
|
|
if (!NT_SUCCESS(Status = RtlpBadDosRootPathToEmptyString(0, &CheckRootString))) {
|
|
goto Exit;
|
|
}
|
|
*LengthOut = RTL_STRING_GET_LENGTH_CHARS(&CheckRootString);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
|
|
"%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status);
|
|
#endif
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAppendPathElement(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_UNICODE_STRING_BUFFER Path,
|
|
PCUNICODE_STRING ConstElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function appends a path element to a path.
|
|
For now, like:
|
|
typedef PRTL_UNICODE_STRING_BUFFER PRTL_MUTABLE_PATH;
|
|
typedef PCUNICODE_STRING PCRTL_CONSTANT_PATH_ELEMENT;
|
|
Maybe something higher level in the future.
|
|
|
|
The result with regard to trailing slashes aims to be similar to the inputs.
|
|
If either Path or ConstElement contains a trailing slash, the result has a trailing slash.
|
|
The character used for the in between and trailing slash is picked among the existing
|
|
slashes in the strings.
|
|
|
|
Arguments:
|
|
|
|
Flags - the ever popular "room for future binary compatible expansion"
|
|
|
|
Path -
|
|
a string representing a path using \\ or / as seperators
|
|
|
|
ConstElement -
|
|
a string representing a path element
|
|
this can actually contain multiple \\ or / delimited path elements
|
|
only the start and end of the string are examined for slashes
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
Path is NULL
|
|
or LengthOut is NULL
|
|
STATUS_NO_MEMORY - RtlHeapAllocate failed
|
|
STATUS_NAME_TOO_LONG - the resulting string does not fit in a UNICODE_STRING, due to its
|
|
use of USHORT instead of ULONG or SIZE_T
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING InBetweenSlashString = RtlpEmptyString;
|
|
UNICODE_STRING TrailingSlashString = RtlpEmptyString;
|
|
WCHAR Slashes[] = {0,0,0,0};
|
|
ULONG i;
|
|
UNICODE_STRING PathsToAppend[3]; // possible slash, element, possible slash
|
|
WCHAR PathSeperators[2] = { '/', '\\' };
|
|
const ULONG ValidFlags =
|
|
RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR
|
|
| RTL_APPEND_PATH_ELEMENT_BUGFIX_CHECK_FIRST_THREE_CHARS_FOR_SLASH_TAKE_FOUND_SLASH_INSTEAD_OF_FIRST_CHAR
|
|
;
|
|
const ULONG InvalidFlags = ~ValidFlags;
|
|
|
|
#define LOCAL_IS_PATH_SEPARATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
|
|
|
|
if ( !RTL_SOFT_VERIFY((Flags & InvalidFlags) == 0)
|
|
|| !RTL_SOFT_VERIFY(Path != NULL)
|
|
|| !RTL_SOFT_VERIFY(ConstElement != NULL)
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) != 0) {
|
|
PathSeperators[0] = '\\';
|
|
}
|
|
|
|
if (ConstElement->Length != 0) {
|
|
|
|
UNICODE_STRING Element = *ConstElement;
|
|
|
|
//
|
|
// Note leading and trailing slashes on the inputs.
|
|
// So that we know if an in-between slash is needed, and if a trailing slash is needed,
|
|
// and to guide what sort of slash to place.
|
|
//
|
|
i = 0;
|
|
if (Path->String.Length != 0) {
|
|
ULONG j;
|
|
ULONG Length = Path->String.Length / sizeof(WCHAR);
|
|
//
|
|
// for the sake for dos drive paths, check the first three chars for a slash
|
|
//
|
|
for (j = 0 ; j < 3 && j < Length ; ++j) {
|
|
if (LOCAL_IS_PATH_SEPARATOR(Path->String.Buffer[j])) {
|
|
if (Flags & RTL_APPEND_PATH_ELEMENT_BUGFIX_CHECK_FIRST_THREE_CHARS_FOR_SLASH_TAKE_FOUND_SLASH_INSTEAD_OF_FIRST_CHAR) {
|
|
Slashes[i] = Path->String.Buffer[j];
|
|
break;
|
|
}
|
|
Slashes[i] = Path->String.Buffer[0];
|
|
break;
|
|
}
|
|
}
|
|
i += 1;
|
|
if (LOCAL_IS_PATH_SEPARATOR(Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1])) {
|
|
Slashes[i] = Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1];
|
|
}
|
|
}
|
|
i = 2;
|
|
if (LOCAL_IS_PATH_SEPARATOR(Element.Buffer[0])) {
|
|
Slashes[i] = Element.Buffer[0];
|
|
}
|
|
i += 1;
|
|
if (LOCAL_IS_PATH_SEPARATOR(Element.Buffer[Element.Length/sizeof(WCHAR) - 1])) {
|
|
Slashes[i] = Element.Buffer[Element.Length/sizeof(WCHAR) - 1];
|
|
}
|
|
|
|
if (!Slashes[1] && !Slashes[2]) {
|
|
//
|
|
// first string lacks trailing slash and second string lacks leading slash,
|
|
// must insert one; we favor the types we have, otherwise use a default
|
|
//
|
|
InBetweenSlashString.Length = sizeof(WCHAR);
|
|
InBetweenSlashString.Buffer = RtlPathSeperatorString.Buffer;
|
|
if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
|
|
if (Slashes[3]) {
|
|
InBetweenSlashString.Buffer = &Slashes[3];
|
|
} else if (Slashes[0]) {
|
|
InBetweenSlashString.Buffer = &Slashes[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Slashes[1] && !Slashes[3]) {
|
|
//
|
|
// first string has a trailing slash and second string does not,
|
|
// must add one, the same type
|
|
//
|
|
TrailingSlashString.Length = sizeof(WCHAR);
|
|
if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
|
|
TrailingSlashString.Buffer = &Slashes[1];
|
|
} else {
|
|
TrailingSlashString.Buffer = RtlPathSeperatorString.Buffer;
|
|
}
|
|
}
|
|
|
|
if (Slashes[1] && Slashes[2]) {
|
|
//
|
|
// have both trailing and leading slash, remove leading
|
|
//
|
|
Element.Buffer += 1;
|
|
Element.Length -= sizeof(WCHAR);
|
|
Element.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
i = 0;
|
|
PathsToAppend[i++] = InBetweenSlashString;
|
|
PathsToAppend[i++] = Element;
|
|
PathsToAppend[i++] = TrailingSlashString;
|
|
Status = RtlMultiAppendUnicodeStringBuffer(Path, RTL_NUMBER_OF(PathsToAppend), PathsToAppend);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
|
|
"%s(%d):%s(%wZ, %wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path ? &Path->String : NULL, ConstElement, Status);
|
|
#endif
|
|
return Status;
|
|
#undef LOCAL_IS_PATH_SEPARATOR
|
|
}
|
|
|
|
//
|
|
// FUTURE-2002/02/20-ELi
|
|
// Spelling mistake (Separators)
|
|
// This function does not appear to be used and is exported
|
|
// Figure out if it can be removed
|
|
//
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetLengthWithoutTrailingPathSeperators(
|
|
IN ULONG Flags,
|
|
IN PCUNICODE_STRING Path,
|
|
OUT ULONG* LengthOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function computes the length of the string (in characters) if
|
|
trailing path seperators (\\ and /) are removed.
|
|
|
|
Arguments:
|
|
|
|
Path -
|
|
a string representing a path using \\ or / as seperators
|
|
|
|
LengthOut -
|
|
the length of String (in characters) having removed trailing characters
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
Path is NULL
|
|
or LengthOut is NULL
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index = 0;
|
|
ULONG Length = 0;
|
|
|
|
if (LengthOut != NULL) {
|
|
//
|
|
// Arguably this should be Path->Length / sizeof(*Path->Buffer), but as long
|
|
// as the callstack is all high quality code, it doesn't matter.
|
|
//
|
|
*LengthOut = 0;
|
|
}
|
|
if ( !RTL_SOFT_VERIFY(Path != NULL)
|
|
|| !RTL_SOFT_VERIFY(LengthOut != NULL)
|
|
|| !RTL_SOFT_VERIFY(Flags == 0)
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
Length = Path->Length / sizeof(*Path->Buffer);
|
|
for (Index = Length ; Index != 0 ; --Index) {
|
|
if (!RTL_IS_PATH_SEPARATOR(Path->Buffer[Index - 1])) {
|
|
break;
|
|
}
|
|
}
|
|
//*LengthOut = (Length - Index);
|
|
*LengthOut = Index;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
|
|
"%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status);
|
|
#endif
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpApplyLengthFunction(
|
|
IN ULONG Flags,
|
|
IN SIZE_T SizeOfStruct,
|
|
IN OUT PVOID UnicodeStringOrUnicodeStringBuffer,
|
|
NTSTATUS (NTAPI* LengthFunction)(ULONG, PCUNICODE_STRING, ULONG*)
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is common code for patterns like
|
|
#define RtlRemoveTrailingPathSeperators(Path_) \
|
|
(RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutTrailingPathSeperators))
|
|
|
|
#define RtlRemoveLastPathElement(Path_) \
|
|
(RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutLastPathElement))
|
|
|
|
Note that shortening a UNICODE_STRING only changes the length, whereas
|
|
shortening a RTL_UNICODE_STRING_BUFFER writes a terminal nul.
|
|
|
|
I expect this pattern will be less error prone than having clients pass the UNICODE_STRING
|
|
contained in the RTL_UNICODE_STRING_BUFFER followed by calling RTL_NUL_TERMINATE_STRING.
|
|
|
|
And, that pattern cannot be inlined with a macro while also preserving that we
|
|
return an NTSTATUS.
|
|
|
|
Arguments:
|
|
|
|
Flags - the ever popular "room for future binary compatible expansion"
|
|
|
|
UnicodeStringOrUnicodeStringBuffer -
|
|
a PUNICODE_STRING or PRTL_UNICODE_STRING_BUFFER, as indicated by
|
|
SizeOfStruct
|
|
|
|
SizeOfStruct -
|
|
a rough type indicator of UnicodeStringOrUnicodeStringBuffer, to allow for overloading in C
|
|
|
|
LengthFunction -
|
|
computes a length for UnicodeStringOrUnicodeStringBuffer to be shortened too
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
STATUS_INVALID_PARAMETER -
|
|
SizeOfStruct not one of the expected sizes
|
|
or LengthFunction is NULL
|
|
or UnicodeStringOrUnicodeStringBuffer is NULL
|
|
|
|
|
|
--*/
|
|
{
|
|
PUNICODE_STRING UnicodeString = NULL;
|
|
PRTL_UNICODE_STRING_BUFFER UnicodeStringBuffer = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Length = 0;
|
|
|
|
if (!RTL_SOFT_VERIFY(UnicodeStringOrUnicodeStringBuffer != NULL)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
if (!RTL_SOFT_VERIFY(LengthFunction != NULL)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
if (!RTL_SOFT_VERIFY(Flags == 0)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
switch (SizeOfStruct)
|
|
{
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
case sizeof(*UnicodeString):
|
|
UnicodeString = UnicodeStringOrUnicodeStringBuffer;
|
|
break;
|
|
case sizeof(*UnicodeStringBuffer):
|
|
UnicodeStringBuffer = UnicodeStringOrUnicodeStringBuffer;
|
|
UnicodeString = &UnicodeStringBuffer->String;
|
|
break;
|
|
}
|
|
|
|
Status = (*LengthFunction)(Flags, UnicodeString, &Length);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (Length > (UNICODE_STRING_MAX_BYTES / sizeof(UnicodeString->Buffer[0])) ) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
UnicodeString->Length = (USHORT)(Length * sizeof(UnicodeString->Buffer[0]));
|
|
if (UnicodeStringBuffer != NULL) {
|
|
RTL_NUL_TERMINATE_STRING(UnicodeString);
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
|
|
"%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, UnicodeString, Status);
|
|
#endif
|
|
return Status;
|
|
}
|