|
|
// 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; }
|