* PROGRAMMERS: Wine team
* Thomas Weidenmueller
* Gunnar Dalsnes
+ * Alex Ionescu (alex.ionescu@reactos.org)
+ * Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
+#define RTL_CURDIR_IS_REMOVABLE 0x1
+#define RTL_CURDIR_DROP_OLD_HANDLE 0x2
+#define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
+C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
+
/* GLOBALS ********************************************************************/
static const WCHAR DeviceRootW[] = L"\\\\.\\";
-static const UNICODE_STRING _condev = RTL_CONSTANT_STRING(L"\\\\.\\CON");
-static const UNICODE_STRING _unc = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
-
const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
+const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
+const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\");
+const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON");
+const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\");
+
const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");
const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");
const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN");
const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");
const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
-const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON");
const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
-/* FUNCTIONS *****************************************************************/
-
-/*
- * @implemented
- */
-VOID
-NTAPI
-RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
-{
- /* Check if a directory reference was grabbed */
- if (RelativeName->CurDirRef)
- {
- /* FIXME: Not yet supported */
- UNIMPLEMENTED;
- RelativeName->CurDirRef = NULL;
- }
-}
-
-/*
- * @implemented
- */
-ULONG
-NTAPI
-RtlGetLongestNtPathLength(VOID)
-{
- /*
- * The longest NT path is a DOS path that actually sits on a UNC path (ie:
- * a mapped network drive), which is accessed through the DOS Global?? path.
- * This is, and has always been equal to, 269 characters, except in Wine
- * which claims this is 277. Go figure.
- */
- return (MAX_PATH + _unc.Length + sizeof(ANSI_NULL));
-}
-
-
-/*
- * @implemented
- */
-ULONG
-NTAPI
-RtlDetermineDosPathNameType_U(IN PCWSTR Path)
-{
- DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
- ASSERT(Path != NULL);
+PRTLP_CURDIR_REF RtlpCurDirRef;
- /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
- if (IS_PATH_SEPARATOR(Path[0]))
- {
- if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */
- if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */
- if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
- if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
- return RtlPathTypeRootLocalDevice; /* \\. or \\? */
- }
- else
- {
- if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
- if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */
- return RtlPathTypeDriveRelative; /* x: */
- }
-}
+/* PRIVATE FUNCTIONS **********************************************************/
-/*
- * @implemented
- */
RTL_PATH_TYPE
NTAPI
RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
{
- PWCHAR Path = PathString->Buffer;
- ULONG Chars = PathString->Length / sizeof(WCHAR);
+ PWCHAR Path;
+ ULONG Chars;
+
+ Path = PathString->Buffer;
+ Chars = PathString->Length / sizeof(WCHAR);
+
+ /* Return if there are no characters */
+ if (!Chars) return RtlPathTypeRelative;
/*
* The algorithm is similar to RtlDetermineDosPathNameType_U but here we
* actually check for the path length before touching the characters
*/
- if ((Chars < 1) || (IS_PATH_SEPARATOR(Path[0])))
+ if (IS_PATH_SEPARATOR(Path[0]))
{
if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */
if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */
}
else
{
- if ((Chars < 2) || (!(Path[0]) || (Path[1] != L':'))) return RtlPathTypeRelative; /* x */
- if ((Chars < 3) || (IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveAbsolute; /* x:\ */
- return RtlPathTypeDriveRelative; /* x: */
+ if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
+ if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */
+ return RtlPathTypeDriveAbsolute; /* x:\ */
}
}
-/*
- * @implemented
- */
ULONG
NTAPI
-RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
+RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
{
UNICODE_STRING PathCopy;
PWCHAR Start, End;
- ULONG PathChars, ColonCount = 0;
- USHORT ReturnOffset = 0, ReturnLength;
+ USHORT PathChars, ColonCount = 0;
+ USHORT ReturnOffset = 0, ReturnLength, OriginalLength;
WCHAR c;
+ /* Validate the input */
+ if (!PathString) return 0;
+
/* Check what type of path this is */
switch (RtlDetermineDosPathNameType_Ustr(PathString))
{
/* Make a copy of the string */
PathCopy = *PathString;
+ OriginalLength = PathString->Length;
/* Return if there's no characters */
PathChars = PathCopy.Length / sizeof(WCHAR);
}
/* Check for extension or space, and truncate */
- c = PathCopy.Buffer[PathChars - 1];
do
{
- /* Stop if we hit a space or period */
- if ((c != '.') && (c != ' ')) break;
+ /* Stop if we hit something else than a space or period */
+ c = PathCopy.Buffer[PathChars - 1];
+ if ((c != L'.') && (c != L' ')) break;
- /* Fixup the lengths and get the next character */
+ /* Fixup the lengths */
PathCopy.Length -= sizeof(WCHAR);
- if (!--PathChars) c = PathCopy.Buffer[PathChars - 1];
/* Remember this for later */
ColonCount++;
- } while (PathChars);
+ } while (--PathChars);
/* Anything still left? */
if (PathChars)
{
/* Check if the character is a path or drive separator */
c = *End;
- if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1)))
+ if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1)))
{
/* Get the next lower case character */
End++;
- c = *End | ' '; // ' ' == ('z' - 'Z')
+ c = RtlDowncaseUnicodeChar(*End);
/* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
- if ((End < &PathCopy.Buffer[PathCopy.Length / sizeof(WCHAR)]) &&
- ((c == 'l') || (c == 'c') || (c == 'p') || (c == 'a') || (c == 'n')))
+ if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) &&
+ ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n')))
{
/* Calculate the offset */
- ReturnOffset = (PCHAR)End - (PCHAR)PathCopy.Buffer;
+ ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
/* Build the final string */
- PathCopy.Length -= ReturnOffset;
- PathCopy.Length -= (ColonCount * sizeof(WCHAR));
+ PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
PathCopy.Buffer = End;
+
+ /* Save new amount of chars in the path */
+ PathChars = PathCopy.Length / sizeof(WCHAR);
+
break;
}
+ else
+ {
+ return 0;
+ }
}
-
- return 0;
}
/* Get the next lower case character and check if it's a DOS device */
- c = *PathCopy.Buffer | ' '; // ' ' == ('z' - 'Z')
- if ((c != 'l') && (c != 'c') && (c != 'p') && (c != 'a') && (c != 'n'))
+ c = RtlDowncaseUnicodeChar(*PathCopy.Buffer);
+ if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n'))
{
/* Not LPT, COM, PRN, AUX, or NUL */
return 0;
while (Start < End)
{
c = *Start;
- if ((c == '.') || (c == ':')) break;
+ if ((c == L'.') || (c == L':')) break;
Start++;
}
/* And then go backwards to get rid of spaces */
- while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start;
+ while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start;
/* Finally see how many characters are left, and that's our size */
- PathChars = Start - PathCopy.Buffer;
+ PathChars = (USHORT)(Start - PathCopy.Buffer);
PathCopy.Length = PathChars * sizeof(WCHAR);
/* Check if this is a COM or LPT port, which has a digit after it */
if ((PathChars == 4) &&
- (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != '0')))
+ (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0')))
{
/* Don't compare the number part, just check for LPT or COM */
PathCopy.Length -= sizeof(WCHAR);
(RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
{
/* Found it */
- ReturnLength = sizeof(L"COM1");
- return MAKELONG(ReturnOffset, ReturnLength);
+ ReturnLength = sizeof(L"COM1") - sizeof(WCHAR);
+ return MAKELONG(ReturnLength, ReturnOffset);
}
}
else if ((PathChars == 3) &&
(RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
{
/* Otherwise this was something like AUX, NUL, PRN, or CON */
- ReturnLength = sizeof(L"AUX");
- return MAKELONG(ReturnOffset, ReturnLength);
+ ReturnLength = sizeof(L"AUX") - sizeof(WCHAR);
+ return MAKELONG(ReturnLength, ReturnOffset);
}
- /* Otherwise, this isn't a valid DOS device */
+ /* Otherwise, this is not a valid DOS device */
return 0;
}
-/*
- * @implemented
- */
-ULONG
+NTSTATUS
NTAPI
-RtlIsDosDeviceName_U(IN PWSTR Path)
+RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
+ IN ULONG Length,
+ OUT PBOOLEAN NameInvalid)
{
- UNICODE_STRING PathString;
+ PWCHAR Buffer;
NTSTATUS Status;
-
- /* Build the string */
- Status = RtlInitUnicodeStringEx(&PathString, Path);
- if (!NT_SUCCESS(Status)) return 0;
-
- /*
- * Returns 0 if name is not valid DOS device name, or DWORD with
- * offset in bytes to DOS device name from beginning of buffer in high word
- * and size in bytes of DOS device name in low word
- */
- return RtlIsDosDeviceName_Ustr(&PathString);
-}
-
-/*
- * @implemented
- */
-ULONG
-NTAPI
-RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
- IN PWSTR Buffer)
-{
- ULONG Length, Bytes;
- PCURDIR CurDir;
- PWSTR CurDirName;
- DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
-
- /* Lock the PEB to get the current directory */
- RtlAcquirePebLock();
- CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
-
- /* Get the buffer and character length */
- CurDirName = CurDir->DosPath.Buffer;
- Length = CurDir->DosPath.Length / sizeof(WCHAR);
- ASSERT((CurDirName != NULL) && (Length > 0));
- /* Check for x:\ vs x:\path\foo (note the trailing slash) */
- Bytes = Length * sizeof(WCHAR);
- if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
- {
- /* Check if caller does not have enough space */
- if (MaximumLength <= Bytes)
- {
- /* Call has no space for it, fail, add the trailing slash */
- RtlReleasePebLock();
- return Bytes + sizeof(L'\\');
- }
- }
- else
+ /* Allocate a large enough buffer */
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length);
+ if (Buffer)
{
- /* Check if caller does not have enough space */
- if (MaximumLength <= Bytes)
- {
- /* Call has no space for it, fail */
- RtlReleasePebLock();
- return Bytes;
- }
- }
+ /* Assume failure */
+ *NameInvalid = TRUE;
- /* Copy the buffer since we seem to have space */
- RtlCopyMemory(Buffer, CurDirName, Bytes);
+ /* Copy the filename */
+ RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length);
- /* The buffer should end with a path separator */
- ASSERT(Buffer[Length - 1] == L'\\');
+ /* And add a dot at the end */
+ Buffer[Length / sizeof(WCHAR)] = L'.';
+ Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
- /* Again check for our two cases (drive root vs path) */
- if ((Length <= 1) || (Buffer[Length - 2] != L':'))
- {
- /* Replace the trailing slash with a null */
- Buffer[Length - 1] = UNICODE_NULL;
- --Length;
+ /* Check if the file exists or not */
+ *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE;
+
+ /* Get rid of the buffer now */
+ Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
}
else
{
- /* Append the null char since there's no trailing slash */
- Buffer[Length] = UNICODE_NULL;
+ /* Assume the name is ok, but fail the call */
+ *NameInvalid = FALSE;
+ Status = STATUS_NO_MEMORY;
}
- /* Release PEB lock */
- RtlReleasePebLock();
- DPRINT("CurrentDirectory %S\n", Buffer);
- return Length * sizeof(WCHAR);
+ /* Return the status */
+ return Status;
}
-/*
- * @implemented
- */
-NTSTATUS NTAPI
-RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
-{
- UNICODE_STRING full;
- FILE_FS_DEVICE_INFORMATION device_info;
- OBJECT_ATTRIBUTES Attr;
- IO_STATUS_BLOCK iosb;
- PCURDIR cd;
- NTSTATUS Status;
- ULONG size;
- HANDLE handle = NULL;
- PWSTR ptr;
-
- DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
-
- full.Buffer = NULL;
-
- RtlAcquirePebLock ();
-
- cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
-
- if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
- {
- RtlReleasePebLock ();
- return STATUS_OBJECT_NAME_INVALID;
- }
-
- DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
-
- InitializeObjectAttributes (&Attr,
- &full,
- OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
- NULL,
- NULL);
-
- Status = ZwOpenFile (&handle,
- SYNCHRONIZE | FILE_TRAVERSE,
- &Attr,
- &iosb,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
-
- if (!NT_SUCCESS(Status))
- {
- RtlFreeUnicodeString( &full);
- RtlReleasePebLock ();
- return Status;
- }
-
- /* don't keep the directory handle open on removable media */
- if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
- sizeof(device_info), FileFsDeviceInformation )) &&
- (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
- {
- DPRINT1("don't keep the directory handle open on removable media\n");
- ZwClose( handle );
- handle = 0;
- }
-
- if (cd->Handle)
- ZwClose(cd->Handle);
- cd->Handle = handle;
-
- /* append trailing \ if missing */
- size = full.Length / sizeof(WCHAR);
- ptr = full.Buffer;
- ptr += 4; /* skip \??\ prefix */
- size -= 4;
-
- /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
- * So the nullterm is replaced with \
- * -Gunnar
- */
- if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
-
- memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
- cd->DosPath.Buffer[size] = 0;
- cd->DosPath.Length = size * sizeof(WCHAR);
-
- RtlFreeUnicodeString( &full);
- RtlReleasePebLock();
-
- return STATUS_SUCCESS;
-}
/******************************************************************
- * collapse_path
+ * RtlpCollapsePath (from WINE)
*
* Helper for RtlGetFullPathName_U.
- * Get rid of . and .. components in the path.
+ * 1) Convert slashes into backslashes
+ * 2) Get rid of duplicate backslashes
+ * 3) Get rid of . and .. components in the path.
*/
-void FORCEINLINE collapse_path( WCHAR *path, UINT mark )
+static VOID
+RtlpCollapsePath(PWSTR path, ULONG mark, BOOLEAN SkipTrailingPathSeparators)
{
- WCHAR *p, *next;
+ PWSTR p, next;
/* convert every / into a \ */
- for (p = path; *p; p++) if (*p == '/') *p = '\\';
+ for (p = path; *p; p++)
+ {
+ if (*p == L'/') *p = L'\\';
+ }
/* collapse duplicate backslashes */
next = path + max( 1, mark );
- for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
- *next = 0;
+ for (p = next; *p; p++)
+ {
+ if (*p != L'\\' || next[-1] != L'\\') *next++ = *p;
+ }
+ *next = UNICODE_NULL;
p = path + mark;
while (*p)
{
- if (*p == '.')
+ if (*p == L'.')
{
switch(p[1])
{
- case '\\': /* .\ component */
- next = p + 2;
- memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
- continue;
- case 0: /* final . */
- if (p > path + mark) p--;
- *p = 0;
- continue;
- case '.':
- if (p[2] == '\\') /* ..\ component */
- {
- next = p + 3;
- if (p > path + mark)
+ case UNICODE_NULL: /* final . */
+ if (p > path + mark) p--;
+ *p = UNICODE_NULL;
+ continue;
+ case L'\\': /* .\ component */
+ next = p + 2;
+ RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+ continue;
+ case L'.':
+ if (p[2] == L'\\') /* ..\ component */
{
- p--;
- while (p > path + mark && p[-1] != '\\') p--;
+ next = p + 3;
+ if (p > path + mark)
+ {
+ p--;
+ while (p > path + mark && p[-1] != L'\\') p--;
+ }
+ RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+ continue;
}
- memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
- continue;
- }
- else if (!p[2]) /* final .. */
- {
- if (p > path + mark)
+ else if (!p[2]) /* final .. */
{
- p--;
- while (p > path + mark && p[-1] != '\\') p--;
- if (p > path + mark) p--;
+ if (p > path + mark)
+ {
+ p--;
+ while (p > path + mark && p[-1] != L'\\') p--;
+ if (p > path + mark) p--;
+ }
+ *p = UNICODE_NULL;
+ continue;
}
- *p = 0;
- continue;
- }
- break;
+ break;
}
}
/* skip to the next component */
- while (*p && *p != '\\') p++;
- if (*p == '\\')
+ while (*p && *p != L'\\') p++;
+ if (*p == L'\\')
{
/* remove last dot in previous dir name */
- if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
- else p++;
+ if (p > path + mark && p[-1] == L'.')
+ RtlMoveMemory( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
+ else
+ p++;
}
}
- /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
- while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
- *p = 0;
-}
-
+ /* Remove trailing backslashes if needed (after the UNC part if it exists) */
+ if (SkipTrailingPathSeparators)
+ {
+ while (p > path + mark && IS_PATH_SEPARATOR(p[-1])) *p-- = UNICODE_NULL;
+ }
+ /* Remove trailing spaces and dots (for all the path) */
+ while (p > path && (p[-1] == L' ' || p[-1] == L'.')) *p-- = UNICODE_NULL;
-/******************************************************************
- * skip_unc_prefix
- *
- * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
- */
-static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
-{
- ptr += 2;
- while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */
- while (IS_PATH_SEPARATOR(*ptr)) ptr++;
- while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */
- while (IS_PATH_SEPARATOR(*ptr)) ptr++;
- return ptr;
+ /* Null-terminate the string */
+ *p = UNICODE_NULL;
}
-
/******************************************************************
- * get_full_path_helper
+ * RtlpSkipUNCPrefix (from WINE)
*
* Helper for RtlGetFullPathName_U
- * Note: name and buffer are allowed to point to the same memory spot
+ * Skip the \\share\dir part of a file name and return the new position
+ * (which can point on the last backslash of "dir\")
*/
-static ULONG get_full_path_helper(
- LPCWSTR name,
- LPWSTR buffer,
- ULONG size)
+static SIZE_T
+RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
{
- ULONG reqsize = 0, mark = 0, dep = 0, deplen;
- LPWSTR ins_str = NULL;
- LPCWSTR ptr;
- const UNICODE_STRING* cd;
- WCHAR tmp[4];
+ PCWSTR UncPath = FileNameBuffer + 2;
+ DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
- /* return error if name only consists of spaces */
- for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
- if (!*ptr) return 0;
+ while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* share name */
+ while (IS_PATH_SEPARATOR(*UncPath)) UncPath++;
+ while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* dir name */
+ /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
- RtlAcquirePebLock();
+ return (UncPath - FileNameBuffer);
+}
- //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
- cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath;
+NTSTATUS
+NTAPI
+RtlpApplyLengthFunction(IN ULONG Flags,
+ IN ULONG Type,
+ IN PVOID UnicodeStringOrUnicodeStringBuffer,
+ IN PVOID LengthFunction)
+{
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
- switch (RtlDetermineDosPathNameType_U(name))
- {
- case RtlPathTypeUncAbsolute: /* \\foo */
- ptr = skip_unc_prefix( name );
- mark = (ptr - name);
- break;
+NTSTATUS
+NTAPI
+RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags,
+ IN PWCHAR Path,
+ OUT PULONG LengthOut)
+{
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName,
+ IN PUNICODE_STRING a2,
+ IN PUNICODE_STRING a3)
+{
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
- case RtlPathTypeLocalDevice: /* \\.\foo */
- mark = 4;
- break;
+ULONG
+NTAPI
+RtlGetFullPathName_Ustr(
+ _In_ PUNICODE_STRING FileName,
+ _In_ ULONG Size,
+ _Out_z_bytecap_(Size) PWSTR Buffer,
+ _Out_opt_ PCWSTR *ShortName,
+ _Out_opt_ PBOOLEAN InvalidName,
+ _Out_ RTL_PATH_TYPE *PathType)
+{
+ NTSTATUS Status;
+ PWCHAR FileNameBuffer;
+ ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength;
+ BOOLEAN SkipTrailingPathSeparators;
+ WCHAR c;
- case RtlPathTypeDriveAbsolute: /* c:\foo */
- reqsize = sizeof(WCHAR);
- tmp[0] = towupper(name[0]);
- ins_str = tmp;
- dep = 1;
- mark = 3;
- break;
- case RtlPathTypeDriveRelative: /* c:foo */
- dep = 2;
- if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
- {
- UNICODE_STRING var, val;
-
- tmp[0] = '=';
- tmp[1] = name[0];
- tmp[2] = ':';
- tmp[3] = '\0';
- var.Length = 3 * sizeof(WCHAR);
- var.MaximumLength = 4 * sizeof(WCHAR);
- var.Buffer = tmp;
- val.Length = 0;
- val.MaximumLength = size;
- val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
- if (val.Buffer == NULL)
- {
- reqsize = 0;
- goto done;
- }
+ ULONG reqsize = 0;
+ PCWSTR ptr;
- switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
- {
- case STATUS_SUCCESS:
- /* FIXME: Win2k seems to check that the environment variable actually points
- * to an existing directory. If not, root of the drive is used
- * (this seems also to be the only spot in RtlGetFullPathName that the
- * existence of a part of a path is checked)
- */
- /* fall thru */
- case STATUS_BUFFER_TOO_SMALL:
- reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
- val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
- ins_str = val.Buffer;
- break;
- case STATUS_VARIABLE_NOT_FOUND:
- reqsize = 3 * sizeof(WCHAR);
- tmp[0] = name[0];
- tmp[1] = ':';
- tmp[2] = '\\';
- ins_str = tmp;
- RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
- break;
- default:
- DPRINT1("Unsupported status code\n");
- RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
- break;
- }
- mark = 3;
- break;
- }
- /* fall through */
+ PCUNICODE_STRING CurDirName;
+ UNICODE_STRING EnvVarName, EnvVarValue;
+ WCHAR EnvVarNameBuffer[4];
- case RtlPathTypeRelative: /* foo */
- reqsize = cd->Length;
- ins_str = cd->Buffer;
- if (cd->Buffer[1] != ':')
- {
- ptr = skip_unc_prefix( cd->Buffer );
- mark = ptr - cd->Buffer;
- }
- else mark = 3;
- break;
+ ULONG PrefixCut = 0; // Where the path really starts (after the skipped prefix)
+ PWCHAR Prefix = NULL; // pointer to the string to be inserted as the new path prefix
+ ULONG PrefixLength = 0;
+ PWCHAR Source;
+ ULONG SourceLength;
- case RtlPathTypeRooted: /* \xxx */
-#ifdef __WINE__
- if (name[0] == '/') /* may be a Unix path */
- {
- const WCHAR *ptr = name;
- int drive = find_drive_root( &ptr );
- if (drive != -1)
- {
- reqsize = 3 * sizeof(WCHAR);
- tmp[0] = 'A' + drive;
- tmp[1] = ':';
- tmp[2] = '\\';
- ins_str = tmp;
- mark = 3;
- dep = ptr - name;
- break;
- }
- }
-#endif
- if (cd->Buffer[1] == ':')
- {
- reqsize = 2 * sizeof(WCHAR);
- tmp[0] = cd->Buffer[0];
- tmp[1] = ':';
- ins_str = tmp;
- mark = 3;
- }
- else
- {
- ptr = skip_unc_prefix( cd->Buffer );
- reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
- mark = reqsize / sizeof(WCHAR);
- ins_str = cd->Buffer;
- }
- break;
- case RtlPathTypeRootLocalDevice: /* \\. */
- reqsize = 4 * sizeof(WCHAR);
- dep = 3;
- tmp[0] = '\\';
- tmp[1] = '\\';
- tmp[2] = '.';
- tmp[3] = '\\';
- ins_str = tmp;
- mark = 4;
- break;
+ /* For now, assume the name is valid */
+ DPRINT("Filename: %wZ\n", FileName);
+ DPRINT("Size and buffer: %lx %p\n", Size, Buffer);
+ if (InvalidName) *InvalidName = FALSE;
- case RtlPathTypeUnknown:
- goto done;
- }
+ /* Handle initial path type and failure case */
+ *PathType = RtlPathTypeUnknown;
+ if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
- /* enough space ? */
- deplen = wcslen(name + dep) * sizeof(WCHAR);
- if (reqsize + deplen + sizeof(WCHAR) > size)
+ /* Break filename into component parts */
+ FileNameBuffer = FileName->Buffer;
+ FileNameLength = FileName->Length;
+ FileNameChars = FileNameLength / sizeof(WCHAR);
+
+ /* Kill trailing spaces */
+ c = FileNameBuffer[FileNameChars - 1];
+ while ((FileNameLength != 0) && (c == L' '))
{
- /* not enough space, return need size (including terminating '\0') */
- reqsize += deplen + sizeof(WCHAR);
- goto done;
+ /* Keep going, ignoring the spaces */
+ FileNameLength -= sizeof(WCHAR);
+ if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
}
- memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
- if (reqsize) memcpy(buffer, ins_str, reqsize);
- reqsize += deplen;
-
- if (ins_str != tmp && ins_str != cd->Buffer)
- RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
+ /* Check if anything is left */
+ if (FileNameLength == 0) return 0;
- collapse_path( buffer, mark );
- reqsize = wcslen(buffer) * sizeof(WCHAR);
+ /*
+ * Check whether we'll need to skip trailing path separators in the
+ * computed full path name. If the original file name already contained
+ * trailing separators, then we keep them in the full path name. On the
+ * other hand, if the original name didn't contain any trailing separators
+ * then we'll skip it in the full path name.
+ */
+ SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]);
-done:
- RtlReleasePebLock();
- return reqsize;
-}
+ /* Check if this is a DOS name */
+ DosLength = RtlIsDosDeviceName_Ustr(FileName);
+ DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
+ if (DosLength != 0)
+ {
+ /* Zero out the short name */
+ if (ShortName) *ShortName = NULL;
+ /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
+ DosLengthOffset = HIWORD(DosLength);
+ DosLength = LOWORD(DosLength);
-/******************************************************************
- * RtlGetFullPathName_U (NTDLL.@)
- *
- * Returns the number of bytes written to buffer (not including the
- * terminating NULL) if the function succeeds, or the required number of bytes
- * (including the terminating NULL) if the buffer is too small.
- *
- * file_part will point to the filename part inside buffer (except if we use
- * DOS device name, in which case file_in_buf is NULL)
- *
- * @implemented
- */
-ULONG NTAPI RtlGetFullPathName_U(
- const WCHAR* name,
- ULONG size,
- WCHAR* buffer,
- WCHAR** file_part)
-{
- WCHAR* ptr;
- ULONG dosdev;
- ULONG reqsize;
+ /* Do we have a DOS length, and does the caller want validity? */
+ if (InvalidName && (DosLengthOffset != 0))
+ {
+ /* Do the check */
+ Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
- DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
+ /* If the check failed, or the name is invalid, fail here */
+ if (!NT_SUCCESS(Status)) return 0;
+ if (*InvalidName) return 0;
+ }
- if (!name || !*name) return 0;
+ /* Add the size of the device root and check if it fits in the size */
+ FullLength = DosLength + DeviceRootString.Length;
+ if (FullLength < Size)
+ {
+ /* Add the device string */
+ RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length);
- if (file_part) *file_part = NULL;
+ /* Now add the DOS device name */
+ RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length,
+ (PCHAR)FileNameBuffer + DosLengthOffset,
+ DosLength);
- /* check for DOS device name */
- dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
- if (dosdev)
- {
- DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
- DWORD sz = LOWORD(dosdev); /* in bytes */
+ /* Null terminate */
+ *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL;
+ return FullLength;
+ }
- if (8 + sz + 2 > size) return sz + 10;
- wcscpy(buffer, DeviceRootW);
- memmove(buffer + 4, name + offset, sz);
- buffer[4 + sz / sizeof(WCHAR)] = '\0';
- /* file_part isn't set in this case */
- return sz + 8;
+ /* Otherwise, there's no space, so return the buffer size needed */
+ if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0;
+ return FullLength + sizeof(UNICODE_NULL);
}
- reqsize = get_full_path_helper(name, buffer, size);
- if (!reqsize) return 0;
- if (reqsize > size)
- {
- LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
- if (tmp == NULL) return 0;
- reqsize = get_full_path_helper(name, tmp, reqsize);
- if (reqsize + sizeof(WCHAR) > size) /* it may have worked the second time */
- {
- RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
- return reqsize + sizeof(WCHAR);
- }
- memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
- RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
- }
+ /* Zero out the destination buffer. FileName must be different from Buffer */
+ RtlZeroMemory(Buffer, Size);
- /* find file part */
- if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
- *file_part = ptr;
- return reqsize;
-}
+ /* Get the path type */
+ *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
-/*
- * @implemented
- */
-BOOLEAN NTAPI
-RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName,
- OUT PUNICODE_STRING NtPathName,
- OUT PCWSTR *NtFileNamePart,
- OUT PRTL_RELATIVE_NAME_U DirectoryInfo)
-{
- UNICODE_STRING us;
- PCURDIR cd;
- ULONG Type;
- ULONG Size;
- ULONG Length;
- ULONG tmpLength;
- ULONG Offset;
- WCHAR fullname[MAX_PATH + 1];
- PWSTR Buffer = NULL;
-
- RtlInitUnicodeString (&us, DosPathName);
- if (us.Length > 8)
- {
- Buffer = us.Buffer;
- /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
- if (Buffer[0] == L'\\' && Buffer[1] == L'\\' &&
- Buffer[2] == L'?' && Buffer[3] == L'\\')
- {
- /* allocate the new string and simply copy it */
- NtPathName->Length = us.Length;
- NtPathName->MaximumLength = us.Length + sizeof(WCHAR);
- NtPathName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- NtPathName->MaximumLength);
- if (NtPathName->Buffer == NULL)
- {
- return FALSE;
- }
-
- /* copy the string */
- RtlCopyMemory(NtPathName->Buffer,
- us.Buffer,
- NtPathName->Length);
- NtPathName->Buffer[us.Length / sizeof(WCHAR)] = L'\0';
-
- /* change the \\?\ prefix to \??\ */
- NtPathName->Buffer[1] = L'?';
-
- if (NtFileNamePart != NULL)
- {
- PWSTR FilePart = NULL;
- PWSTR s;
-
- /* try to find the last separator */
- s = NtPathName->Buffer + (NtPathName->Length / sizeof(WCHAR));
- while (s != NtPathName->Buffer)
- {
- if (*s == L'\\')
- {
- FilePart = s + 1;
- break;
- }
- s--;
- }
-
- *NtFileNamePart = FilePart;
- }
-
- if (DirectoryInfo != NULL)
- {
- DirectoryInfo->RelativeName.Length = 0;
- DirectoryInfo->RelativeName.MaximumLength = 0;
- DirectoryInfo->RelativeName.Buffer = NULL;
- DirectoryInfo->ContainingDirectory = NULL;
- }
-
- return TRUE;
- }
- }
-
- Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
- 0,
- sizeof( fullname ) + MAX_PFX_SIZE);
- if (Buffer == NULL)
- {
- return FALSE;
- }
-
- RtlAcquirePebLock ();
-
- Size = RtlGetFullPathName_U (DosPathName,
- sizeof(fullname),
- fullname,
- (PWSTR*)NtFileNamePart);
- if (Size == 0 || Size > MAX_PATH * sizeof(WCHAR))
- {
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- Buffer);
- RtlReleasePebLock ();
- return FALSE;
- }
-
- /* Set NT prefix */
- Offset = 0;
- memcpy (Buffer, L"\\??\\", 4 * sizeof(WCHAR));
- tmpLength = 4;
-
- Type = RtlDetermineDosPathNameType_U (fullname);
- switch (Type)
- {
- case 1:
- memcpy (Buffer + tmpLength, L"UNC\\", 4 * sizeof(WCHAR));
- tmpLength += 4;
- Offset = 2;
- break; /* \\xxx */
-
- case 6:
- Offset = 4;
- break; /* \\.\xxx */
- }
- Length = wcslen(fullname + Offset);
- memcpy (Buffer + tmpLength, fullname + Offset, (Length + 1) * sizeof(WCHAR));
- Length += tmpLength;
- if (Type == RtlPathTypeDriveAbsolute ||
- Type == RtlPathTypeDriveRelative)
- {
- /* make the drive letter to uppercase */
- Buffer[tmpLength] = towupper(Buffer[tmpLength]);
- }
-
- /* set NT filename */
- NtPathName->Length = Length * sizeof(WCHAR);
- NtPathName->MaximumLength = sizeof(fullname) + MAX_PFX_SIZE;
- NtPathName->Buffer = Buffer;
-
- /* set pointer to file part if possible */
- if (NtFileNamePart && *NtFileNamePart)
- *NtFileNamePart = Buffer + Length - wcslen (*NtFileNamePart);
-
- /* Set name and handle structure if possible */
- if (DirectoryInfo)
- {
- memset (DirectoryInfo, 0, sizeof(RTL_RELATIVE_NAME_U));
- cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
- if (Type == 5 && cd->Handle)
- {
- RtlInitUnicodeString(&us, fullname);
- if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
- {
- Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
- DirectoryInfo->RelativeName.Buffer = Buffer + Length;
- DirectoryInfo->RelativeName.Length = NtPathName->Length - (Length * sizeof(WCHAR));
- DirectoryInfo->RelativeName.MaximumLength = DirectoryInfo->RelativeName.Length;
- DirectoryInfo->ContainingDirectory = cd->Handle;
- }
- }
- }
-
- RtlReleasePebLock();
- return TRUE;
-}
+ /**********************************************
+ ** CODE REWRITTEN IS HAPPENING THERE **
+ **********************************************/
+ Source = FileNameBuffer;
+ SourceLength = FileNameLength;
+ EnvVarValue.Buffer = NULL;
-/*
- * @implemented
- */
-/******************************************************************
- * RtlDosSearchPath_U
- *
- * Searches a file of name 'name' into a ';' separated list of paths
- * (stored in paths)
- * Doesn't seem to search elsewhere than the paths list
- * Stores the result in buffer (file_part will point to the position
- * of the file name in the buffer)
- * FIXME:
- * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
- */
-ULONG
-NTAPI
-RtlDosSearchPath_U(PCWSTR paths,
- PCWSTR search,
- PCWSTR ext,
- ULONG buffer_size,
- PWSTR buffer,
- PWSTR* file_part)
-{
- RTL_PATH_TYPE type = RtlDetermineDosPathNameType_U(search);
- ULONG len = 0;
+ /* Lock the PEB to get the current directory */
+ RtlAcquirePebLock();
+ CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
- if (type == RtlPathTypeRelative)
+ switch (*PathType)
{
- ULONG allocated = 0, needed, filelen;
- WCHAR *name = NULL;
+ case RtlPathTypeUncAbsolute: /* \\foo */
+ PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer);
+ break;
- filelen = 1 /* for \ */ + wcslen(search) + 1 /* \0 */;
+ case RtlPathTypeLocalDevice: /* \\.\foo */
+ PrefixCut = 4;
+ break;
- /* Windows only checks for '.' without worrying about path components */
- if (wcschr( search, '.' )) ext = NULL;
- if (ext != NULL) filelen += wcslen(ext);
+ case RtlPathTypeDriveAbsolute: /* c:\foo */
+ ASSERT(FileNameBuffer[1] == L':');
+ ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
- while (*paths)
- {
- LPCWSTR ptr;
+ Prefix = FileNameBuffer;
+ PrefixLength = 3 * sizeof(WCHAR);
+ Source += 3;
+ SourceLength -= 3 * sizeof(WCHAR);
+
+ PrefixCut = 3;
+ break;
- for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
- if (needed + filelen > allocated)
+ case RtlPathTypeDriveRelative: /* c:foo */
+ Source += 2;
+ SourceLength -= 2 * sizeof(WCHAR);
+ if (RtlUpcaseUnicodeChar(FileNameBuffer[0]) != RtlUpcaseUnicodeChar(CurDirName->Buffer[0]) ||
+ CurDirName->Buffer[1] != L':')
{
- if (!name) name = RtlAllocateHeap(RtlGetProcessHeap(), 0,
- (needed + filelen) * sizeof(WCHAR));
- else
+ EnvVarNameBuffer[0] = L'=';
+ EnvVarNameBuffer[1] = FileNameBuffer[0];
+ EnvVarNameBuffer[2] = L':';
+ EnvVarNameBuffer[3] = UNICODE_NULL;
+
+ EnvVarName.Length = 3 * sizeof(WCHAR);
+ EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR);
+ EnvVarName.Buffer = EnvVarNameBuffer;
+
+ // FIXME: Is it possible to use the user-given buffer ?
+ // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
+ EnvVarValue.Length = 0;
+ EnvVarValue.MaximumLength = (USHORT)Size;
+ EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
+ if (EnvVarValue.Buffer == NULL)
+ {
+ Prefix = NULL;
+ PrefixLength = 0;
+ goto Quit;
+ }
+
+ Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue);
+ switch (Status)
{
- WCHAR *newname = RtlReAllocateHeap(RtlGetProcessHeap(), 0, name,
- (needed + filelen) * sizeof(WCHAR));
- if (!newname) RtlFreeHeap(RtlGetProcessHeap(), 0, name);
- name = newname;
+ case STATUS_SUCCESS:
+ /*
+ * (From Wine)
+ * FIXME: Win2k seems to check that the environment
+ * variable actually points to an existing directory.
+ * If not, root of the drive is used (this seems also
+ * to be the only spot in RtlGetFullPathName that the
+ * existence of a part of a path is checked).
+ */
+ EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\';
+ Prefix = EnvVarValue.Buffer;
+ PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */
+ break;
+
+ case STATUS_BUFFER_TOO_SMALL:
+ reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL);
+ goto Quit;
+
+ default:
+ DPRINT1("RtlQueryEnvironmentVariable_U returned 0x%08lx\n", Status);
+
+ EnvVarNameBuffer[0] = FileNameBuffer[0];
+ EnvVarNameBuffer[1] = L':';
+ EnvVarNameBuffer[2] = L'\\';
+ EnvVarNameBuffer[3] = UNICODE_NULL;
+ Prefix = EnvVarNameBuffer;
+ PrefixLength = 3 * sizeof(WCHAR);
+
+ RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
+ EnvVarValue.Buffer = NULL;
+ break;
}
- if (!name) return 0;
- allocated = needed + filelen;
+ PrefixCut = 3;
+ break;
+ }
+ /* Fall through */
+ DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
+
+ case RtlPathTypeRelative: /* foo */
+ Prefix = CurDirName->Buffer;
+ PrefixLength = CurDirName->Length;
+ if (CurDirName->Buffer[1] != L':')
+ {
+ PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
+ }
+ else
+ {
+ PrefixCut = 3;
+ }
+ break;
+
+ case RtlPathTypeRooted: /* \xxx */
+ if (CurDirName->Buffer[1] == L':')
+ {
+ // The path starts with "C:\"
+ ASSERT(CurDirName->Buffer[1] == L':');
+ ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2]));
+
+ Prefix = CurDirName->Buffer;
+ PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\"
+
+ PrefixCut = 3; // Source index location incremented of + 3
+ }
+ else
+ {
+ PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
+ PrefixLength = PrefixCut * sizeof(WCHAR);
+ Prefix = CurDirName->Buffer;
}
- memmove(name, paths, needed * sizeof(WCHAR));
- /* append '\\' if none is present */
- if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
- wcscpy(&name[needed], search);
- if (ext) wcscat(&name[needed], ext);
- if (RtlDoesFileExists_U(name))
+ break;
+
+ case RtlPathTypeRootLocalDevice: /* \\. */
+ Prefix = DeviceRootString.Buffer;
+ PrefixLength = DeviceRootString.Length;
+ Source += 3;
+ SourceLength -= 3 * sizeof(WCHAR);
+
+ PrefixCut = 4;
+ break;
+
+ case RtlPathTypeUnknown:
+ goto Quit;
+ }
+
+ /* Do we have enough space for storing the full path? */
+ reqsize = PrefixLength;
+ if (reqsize + SourceLength + sizeof(WCHAR) > Size)
+ {
+ /* Not enough space, return needed size (including terminating '\0') */
+ reqsize += SourceLength + sizeof(WCHAR);
+ goto Quit;
+ }
+
+ /*
+ * Build the full path
+ */
+ // if (ShortName) DPRINT1("buffer(1) = %S\n", Buffer);
+ /* Copy the path's prefix */
+ if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength);
+ // if (ShortName) DPRINT1("buffer(2) = %S\n", Buffer);
+ /* Copy the remaining part of the path */
+ RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
+ // if (ShortName) DPRINT1("buffer(3) = %S\n", Buffer);
+
+ /* Some cleanup */
+ Prefix = NULL;
+ if (EnvVarValue.Buffer)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
+ EnvVarValue.Buffer = NULL;
+ }
+
+ /*
+ * Finally, put the path in canonical form,
+ * i.e. simplify redundant . and .., etc...
+ */
+ // if (*PathType == RtlPathTypeUncAbsolute) DPRINT1("RtlpCollapsePath('%S', %lu)\n", Buffer, PrefixCut);
+ RtlpCollapsePath(Buffer, PrefixCut, SkipTrailingPathSeparators);
+ // if (ShortName) DPRINT1("buffer(4) = %S\n", Buffer);
+
+ /* Get the length of the full path name, without its terminating null character */
+ reqsize = wcslen(Buffer) * sizeof(WCHAR);
+
+ /* Find the file part, which is present after the last path separator */
+ if (ShortName)
+ {
+ ptr = wcsrchr(Buffer, L'\\');
+ if (ptr) ++ptr; // Skip it
+
+ /*
+ * For UNC paths, the file part is after the \\share\dir part of the path.
+ */
+ PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3);
+
+ if (ptr && *ptr && (ptr >= Buffer + PrefixCut))
+ {
+ *ShortName = ptr;
+ }
+ else
+ {
+ /* Zero-out the short name */
+ *ShortName = NULL;
+ }
+ }
+
+Quit:
+ /* Release PEB lock */
+ RtlReleasePebLock();
+ return (ULONG)reqsize;
+}
+
+NTSTATUS
+NTAPI
+RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
+ OUT PUNICODE_STRING NtPath,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ ULONG DosLength;
+ PWSTR NewBuffer, p;
+
+ /* Validate the input */
+ if (!DosPath) return STATUS_OBJECT_NAME_INVALID;
+
+ /* Validate the DOS length */
+ DosLength = DosPath->Length;
+ if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
+
+ /* Make space for the new path */
+ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ DosLength + sizeof(UNICODE_NULL));
+ if (!NewBuffer) return STATUS_NO_MEMORY;
+
+ /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
+ RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length);
+ RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length,
+ DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR),
+ DosPath->Length - RtlpDosDevicesPrefix.Length);
+ NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Did the caller send a relative name? */
+ if (RelativeName)
+ {
+ /* Zero initialize it */
+ RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
+ RelativeName->ContainingDirectory = NULL;
+ RelativeName->CurDirRef = 0;
+ }
+
+ /* Did the caller request a partial name? */
+ if (PartName)
+ {
+ /* Loop from the back until we find a path separator */
+ p = &NewBuffer[DosLength / sizeof(WCHAR)];
+ while (--p > NewBuffer)
+ {
+ /* We found a path separator, move past it */
+ if (*p == OBJ_NAME_PATH_SEPARATOR)
{
- len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
+ ++p;
break;
}
- paths = ptr;
}
- RtlFreeHeap(RtlGetProcessHeap(), 0, name);
+
+ /* Check whether a separator was found and if something remains */
+ if ((p > NewBuffer) && *p)
+ {
+ /* What follows the path separator is the partial name */
+ *PartName = p;
+ }
+ else
+ {
+ /* The path ends with a path separator, no partial name */
+ *PartName = NULL;
+ }
+ }
+
+ /* Build the final NT path string */
+ NtPath->Buffer = NewBuffer;
+ NtPath->Length = (USHORT)DosLength;
+ NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
+ IN PCUNICODE_STRING DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ WCHAR BigBuffer[MAX_PATH + 1];
+ PWCHAR PrefixBuffer, NewBuffer, Buffer;
+ ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
+ UNICODE_STRING CapturedDosName, PartNameString, FullPath;
+ BOOLEAN QuickPath;
+ RTL_PATH_TYPE InputPathType, BufferPathType;
+ NTSTATUS Status;
+ BOOLEAN NameInvalid;
+ PCURDIR CurrentDirectory;
+
+ /* Assume MAX_PATH for now */
+ DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
+ HaveRelative, DosName, NtName, PartName, RelativeName);
+ MaxLength = sizeof(BigBuffer);
+
+ /* Validate the input */
+ if (!DosName) return STATUS_OBJECT_NAME_INVALID;
+
+ /* Capture input string */
+ CapturedDosName = *DosName;
+
+ /* Check for the presence or absence of the NT prefix "\\?\" form */
+ // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
+ if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) ||
+ (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) ||
+ (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) ||
+ (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) ||
+ (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3]))
+ {
+ /* NT prefix not present */
+
+ /* Quick path won't be used */
+ QuickPath = FALSE;
+
+ /* Use the static buffer */
+ Buffer = BigBuffer;
+ MaxLength += RtlpDosDevicesUncPrefix.Length;
+
+ /* Allocate a buffer to hold the path */
+ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
+ DPRINT("MaxLength: %lx\n", MaxLength);
+ if (!NewBuffer) return STATUS_NO_MEMORY;
+ }
+ else
+ {
+ /* NT prefix present */
+
+ /* Use the optimized path after acquiring the lock */
+ QuickPath = TRUE;
+ NewBuffer = NULL;
+ }
+
+ /* Lock the PEB and check if the quick path can be used */
+ RtlAcquirePebLock();
+ if (QuickPath)
+ {
+ /* Some simple fixups will get us the correct path */
+ DPRINT("Quick path\n");
+ Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName,
+ NtName,
+ PartName,
+ RelativeName);
+
+ /* Release the lock, we're done here */
+ RtlReleasePebLock();
+ return Status;
+ }
+
+ /* Call the main function to get the full path name and length */
+ PathLength = RtlGetFullPathName_Ustr(&CapturedDosName,
+ MAX_PATH * sizeof(WCHAR),
+ Buffer,
+ PartName,
+ &NameInvalid,
+ &InputPathType);
+ if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR))))
+ {
+ /* Invalid name, fail */
+ DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
+ RtlReleasePebLock();
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /* Start by assuming the path starts with \??\ (DOS Devices Path) */
+ PrefixLength = RtlpDosDevicesPrefix.Length;
+ PrefixBuffer = RtlpDosDevicesPrefix.Buffer;
+ PrefixCut = 0;
+
+ /* Check where it really is */
+ BufferPathType = RtlDetermineDosPathNameType_U(Buffer);
+ DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType);
+ switch (BufferPathType)
+ {
+ /* It's actually a UNC path in \??\UNC\ */
+ case RtlPathTypeUncAbsolute:
+ PrefixLength = RtlpDosDevicesUncPrefix.Length;
+ PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer;
+ PrefixCut = 2;
+ break;
+
+ case RtlPathTypeLocalDevice:
+ /* We made a good guess, go with it but skip the \??\ */
+ PrefixCut = 4;
+ break;
+
+ case RtlPathTypeDriveAbsolute:
+ case RtlPathTypeDriveRelative:
+ case RtlPathTypeRooted:
+ case RtlPathTypeRelative:
+ /* Our guess was good, roll with it */
+ break;
+
+ /* Nothing else is expected */
+ default:
+ ASSERT(FALSE);
+ }
+
+ /* Now copy the prefix and the buffer */
+ RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
+ RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
+ Buffer + PrefixCut,
+ PathLength - (PrefixCut * sizeof(WCHAR)));
+
+ /* Compute the length */
+ Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR);
+ LengthChars = Length / sizeof(WCHAR);
+
+ /* Setup the actual NT path string and terminate it */
+ NtName->Buffer = NewBuffer;
+ NtName->Length = (USHORT)Length;
+ NtName->MaximumLength = (USHORT)MaxLength;
+ NewBuffer[LengthChars] = UNICODE_NULL;
+ DPRINT("New buffer: %S\n", NewBuffer);
+ DPRINT("NT Name: %wZ\n", NtName);
+
+ /* Check if a partial name was requested */
+ if ((PartName) && (*PartName))
+ {
+ /* Convert to Unicode */
+ Status = RtlInitUnicodeStringEx(&PartNameString, *PartName);
+ if (NT_SUCCESS(Status))
+ {
+ /* Set the partial name */
+ *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))];
+ }
+ else
+ {
+ /* Fail */
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
+ RtlReleasePebLock();
+ return Status;
+ }
+ }
+
+ /* Check if a relative name was asked for */
+ if (RelativeName)
+ {
+ /* Setup the structure */
+ RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
+ RelativeName->ContainingDirectory = NULL;
+ RelativeName->CurDirRef = NULL;
+
+ /* Check if the input path itself was relative */
+ if (InputPathType == RtlPathTypeRelative)
+ {
+ /* Get current directory */
+ CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
+ if (CurrentDirectory->Handle)
+ {
+ Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
+ if (!NT_SUCCESS(Status))
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
+ RtlReleasePebLock();
+ return Status;
+ }
+
+ /* If current directory is bigger than full path, there's no way */
+ if (CurrentDirectory->DosPath.Length > FullPath.Length)
+ {
+ RtlReleasePebLock();
+ return Status;
+ }
+
+ /* File is in current directory */
+ if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE))
+ {
+ /* Make relative name string */
+ RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR));
+ RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
+ /* If relative name starts with \, skip it */
+ if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
+ {
+ RelativeName->RelativeName.Buffer++;
+ RelativeName->RelativeName.Length -= sizeof(WCHAR);
+ }
+ RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
+ DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName));
+
+ if (!HaveRelative)
+ {
+ RelativeName->ContainingDirectory = CurrentDirectory->Handle;
+ return Status;
+ }
+
+ /* Give back current directory data & reference counter */
+ RelativeName->CurDirRef = RtlpCurDirRef;
+ if (RelativeName->CurDirRef)
+ {
+ InterlockedIncrement(&RtlpCurDirRef->RefCount);
+ }
+
+ RelativeName->ContainingDirectory = CurrentDirectory->Handle;
+ }
+ }
+ }
}
- else if (RtlDoesFileExists_U(search))
+
+ /* Done */
+ RtlReleasePebLock();
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative,
+ IN PCWSTR DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ NTSTATUS Status;
+ UNICODE_STRING NameString;
+
+ /* Create the unicode name */
+ Status = RtlInitUnicodeStringEx(&NameString, DosName);
+ if (NT_SUCCESS(Status))
{
- len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
+ /* Call the unicode function */
+ Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative,
+ &NameString,
+ NtName,
+ PartName,
+ RelativeName);
}
- return len;
+ /* Return status */
+ return Status;
+}
+
+BOOLEAN
+NTAPI
+RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ /* Call the internal function */
+ ASSERT(RelativeName);
+ return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE,
+ DosName,
+ NtName,
+ PartName,
+ RelativeName));
}
-/*
- * @implemented
- */
BOOLEAN
NTAPI
RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
NTSTATUS Status;
FILE_BASIC_INFORMATION BasicInformation;
-#if 0
/* Get the NT Path */
Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
&NtPathName,
NULL,
&RelativeName);
-#else
- /* FIXME: Use the old API for now */
- Result = RtlDosPathNameToNtPathName_U(FileName->Buffer,
- &NtPathName,
- NULL,
- &RelativeName);
-#endif
if (!Result) return FALSE;
/* Save the buffer */
return RtlDoesFileExists_UstrEx(FileName, TRUE);
}
-/*
- * @implemented
- */
BOOLEAN
NTAPI
RtlDoesFileExists_UEx(IN PCWSTR FileName,
if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName)))
{
/* Call the unicode function */
- return NT_SUCCESS(RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy));
+ return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy);
}
/* Fail */
return FALSE;
}
+/* PUBLIC FUNCTIONS ***********************************************************/
+
/*
* @implemented
*/
-BOOLEAN
+VOID
NTAPI
-RtlDoesFileExists_U(IN PCWSTR FileName)
+RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
{
- /* Call the new function */
- return RtlDoesFileExists_UEx(FileName, TRUE);
+ /* Check if a directory reference was grabbed */
+ if (RelativeName->CurDirRef)
+ {
+ /* Decrease reference count */
+ if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount))
+ {
+ /* If no one uses it any longer, close handle & free */
+ NtClose(RelativeName->CurDirRef->Handle);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef);
+ }
+ RelativeName->CurDirRef = NULL;
+ }
}
/*
- * @unimplemented
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlGetLongestNtPathLength(VOID)
+{
+ /*
+ * The longest NT path is a DOS path that actually sits on a UNC path (ie:
+ * a mapped network drive), which is accessed through the DOS Global?? path.
+ * This is, and has always been equal to, 269 characters, except in Wine
+ * which claims this is 277. Go figure.
+ */
+ return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL);
+}
+
+/*
+ * @implemented
+ * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
+ * (with a 'e' instead of a 'a' in "Seperators").
*/
-BOOLEAN NTAPI
-RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1,
- PVOID Unknown2,
- PVOID Unknown3,
- PVOID Unknown4)
+NTSTATUS
+NTAPI
+RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags,
+ IN PCUNICODE_STRING PathString,
+ OUT PULONG Length)
{
- DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
- Unknown1, Unknown2, Unknown3, Unknown4);
- return FALSE;
+ ULONG NumChars;
+
+ /* Parameters validation */
+ if (Length == NULL) return STATUS_INVALID_PARAMETER;
+
+ *Length = 0;
+
+ if (PathString == NULL) return STATUS_INVALID_PARAMETER;
+
+ /* No flags are supported yet */
+ if (Flags != 0) return STATUS_INVALID_PARAMETER;
+
+ NumChars = PathString->Length / sizeof(WCHAR);
+
+ /*
+ * Notice that we skip the last character, therefore:
+ * - if we have: "some/path/f" we test for: "some/path/"
+ * - if we have: "some/path/" we test for: "some/path"
+ * - if we have: "s" we test for: ""
+ * - if we have: "" then NumChars was already zero and we aren't there
+ */
+
+ while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1]))
+ {
+ --NumChars;
+ }
+
+ *Length = NumChars;
+ return STATUS_SUCCESS;
}
-NTSTATUS NTAPI
-RtlpEnsureBufferSize(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3)
+/*
+ * @implemented
+ */
+RTL_PATH_TYPE
+NTAPI
+RtlDetermineDosPathNameType_U(IN PCWSTR Path)
{
- DPRINT1("RtlpEnsureBufferSize: stub\n");
- return STATUS_NOT_IMPLEMENTED;
+ DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
+
+ /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
+ if (IS_PATH_SEPARATOR(Path[0]))
+ {
+ if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */
+ if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */
+ if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
+ if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
+ return RtlPathTypeRootLocalDevice; /* \\. or \\? */
+ }
+ else
+ {
+ if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
+ if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */
+ return RtlPathTypeDriveRelative; /* x: */
+ }
}
-NTSTATUS NTAPI
-RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlIsDosDeviceName_U(IN PCWSTR Path)
{
- DPRINT1("RtlNtPathNameToDosPathName: stub\n");
- return STATUS_NOT_IMPLEMENTED;
+ UNICODE_STRING PathString;
+ NTSTATUS Status;
+
+ /* Build the string */
+ Status = RtlInitUnicodeStringEx(&PathString, Path);
+ if (!NT_SUCCESS(Status)) return 0;
+
+ /*
+ * Returns 0 if name is not valid DOS device name, or DWORD with
+ * offset in bytes to DOS device name from beginning of buffer in high word
+ * and size in bytes of DOS device name in low word
+ */
+ return RtlIsDosDeviceName_Ustr(&PathString);
+}
+
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlGetCurrentDirectory_U(
+ _In_ ULONG MaximumLength,
+ _Out_bytecap_(MaximumLength) PWSTR Buffer)
+{
+ ULONG Length, Bytes;
+ PCURDIR CurDir;
+ PWSTR CurDirName;
+ DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
+
+ /* Lock the PEB to get the current directory */
+ RtlAcquirePebLock();
+ CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
+
+ /* Get the buffer and character length */
+ CurDirName = CurDir->DosPath.Buffer;
+ Length = CurDir->DosPath.Length / sizeof(WCHAR);
+ ASSERT((CurDirName != NULL) && (Length > 0));
+
+ /*
+ * DosPath.Buffer should always have a trailing slash. There is an assert
+ * below which checks for this.
+ *
+ * This function either returns x:\ for a root (keeping the original buffer)
+ * or it returns x:\path\foo for a directory (replacing the trailing slash
+ * with a NULL.
+ */
+ Bytes = Length * sizeof(WCHAR);
+ if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
+ {
+ /* Check if caller does not have enough space */
+ if (MaximumLength <= Bytes)
+ {
+ /* Call has no space for it, fail, add the trailing slash */
+ RtlReleasePebLock();
+ return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR);
+ }
+ }
+ else
+ {
+ /* Check if caller does not have enough space */
+ if (MaximumLength < Bytes)
+ {
+ /* Call has no space for it, fail */
+ RtlReleasePebLock();
+ return Bytes;
+ }
+ }
+
+ /* Copy the buffer since we seem to have space */
+ RtlCopyMemory(Buffer, CurDirName, Bytes);
+
+ /* The buffer should end with a path separator */
+ ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR);
+
+ /* Again check for our two cases (drive root vs path) */
+ if ((Length <= 1) || (Buffer[Length - 2] != L':'))
+ {
+ /* Replace the trailing slash with a null */
+ Buffer[Length - 1] = UNICODE_NULL;
+ --Length;
+ }
+ else
+ {
+ /* Append the null char since there's no trailing slash */
+ Buffer[Length] = UNICODE_NULL;
+ }
+
+ /* Release PEB lock */
+ RtlReleasePebLock();
+ DPRINT("CurrentDirectory %S\n", Buffer);
+ return Length * sizeof(WCHAR);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
+{
+ PCURDIR CurDir;
+ NTSTATUS Status;
+ RTL_PATH_TYPE PathType;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING FullPath, NtName;
+ PRTLP_CURDIR_REF OldCurDir = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo;
+ ULONG SavedLength, CharLength, FullPathLength;
+ HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL;
+
+ DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
+
+ /* Initialize for failure case */
+ RtlInitEmptyUnicodeString(&NtName, NULL, 0);
+
+ /* Can't set current directory on DOS device */
+ if (RtlIsDosDeviceName_Ustr(Path))
+ {
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* Get current directory */
+ RtlAcquirePebLock();
+ CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
+
+ /* Check if we have to drop current handle */
+ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE)
+ {
+ OldHandle = CurDir->Handle;
+ CurDir->Handle = NULL;
+ }
+
+ /* Allocate a buffer for full path (using max possible length */
+ FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength);
+ if (!FullPath.Buffer)
+ {
+ Status = STATUS_NO_MEMORY;
+ goto Leave;
+ }
+
+ /* Init string */
+ FullPath.Length = 0;
+ FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
+
+ /* Get new directory full path */
+ FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType);
+ if (!FullPathLength)
+ {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Leave;
+ }
+
+ SavedLength = FullPath.MaximumLength;
+ CharLength = FullPathLength / sizeof(WCHAR);
+
+ if (FullPathLength > FullPath.MaximumLength)
+ {
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
+ }
+
+ /* Translate it to NT name */
+ if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
+ {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Leave;
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, &NtName,
+ OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+ NULL, NULL);
+
+ /* If previous current directory was removable, then check it for dropping */
+ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
+ {
+ /* Get back normal handle */
+ CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
+ CurDir->Handle = NULL;
+
+ /* Get device information */
+ Status = NtQueryVolumeInformationFile(CurDirHandle,
+ &IoStatusBlock,
+ &FileFsDeviceInfo,
+ sizeof(FileFsDeviceInfo),
+ FileFsDeviceInformation);
+ /* Retry without taking care of removable device */
+ if (!NT_SUCCESS(Status))
+ {
+ Status = RtlSetCurrentDirectory_U(Path);
+ goto Leave;
+ }
+ }
+ else
+ {
+ /* Open directory */
+ Status = NtOpenFile(&CurDirHandle,
+ SYNCHRONIZE | FILE_TRAVERSE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
+ if (!NT_SUCCESS(Status)) goto Leave;
+
+ /* Get device information */
+ Status = NtQueryVolumeInformationFile(CurDirHandle,
+ &IoStatusBlock,
+ &FileFsDeviceInfo,
+ sizeof(FileFsDeviceInfo),
+ FileFsDeviceInformation);
+ if (!NT_SUCCESS(Status)) goto Leave;
+ }
+
+ /* If device is removable, mark handle */
+ if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
+ {
+ CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
+ }
+
+ FullPath.Length = (USHORT)FullPathLength;
+
+ /* If full path isn't \ terminated, do it */
+ if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR)
+ {
+ if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
+ {
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
+ }
+
+ FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR;
+ FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
+ FullPath.Length += sizeof(WCHAR);
+ }
+
+ /* If we have previous current directory with only us as reference, save it */
+ if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1)
+ {
+ OldCurDirHandle = RtlpCurDirRef->Handle;
+ }
+ else
+ {
+ /* Allocate new current directory struct saving previous one */
+ OldCurDir = RtlpCurDirRef;
+ RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF));
+ if (!RtlpCurDirRef)
+ {
+ RtlpCurDirRef = OldCurDir;
+ OldCurDir = NULL;
+ Status = STATUS_NO_MEMORY;
+ goto Leave;
+ }
+
+ /* Set reference to 1 (us) */
+ RtlpCurDirRef->RefCount = 1;
+ }
+
+ /* Save new data */
+ CurDir->Handle = CurDirHandle;
+ RtlpCurDirRef->Handle = CurDirHandle;
+ CurDirHandle = NULL;
+
+ /* Copy full path */
+ RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR));
+ CurDir->DosPath.Length = FullPath.Length;
+
+ Status = STATUS_SUCCESS;
+
+Leave:
+ RtlReleasePebLock();
+
+ if (FullPath.Buffer)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
+ }
+
+ if (NtName.Buffer)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
+ }
+
+ if (CurDirHandle) NtClose(CurDirHandle);
+
+ if (OldHandle) NtClose(OldHandle);
+
+ if (OldCurDirHandle) NtClose(OldCurDirHandle);
+
+ if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
+ {
+ NtClose(OldCurDir->Handle);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
+ }
+
+ return Status;
+}
+
+
+/******************************************************************
+ * RtlGetFullPathName_U (NTDLL.@)
+ *
+ * Returns the number of bytes written to buffer (not including the
+ * terminating NULL) if the function succeeds, or the required number of bytes
+ * (including the terminating NULL) if the buffer is too small.
+ *
+ * file_part will point to the filename part inside buffer (except if we use
+ * DOS device name, in which case file_in_buf is NULL)
+ *
+ * @implemented
+ */
+
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlGetFullPathName_U(
+ _In_ PCWSTR FileName,
+ _In_ ULONG Size,
+ _Out_z_bytecap_(Size) PWSTR Buffer,
+ _Out_opt_ PWSTR *ShortName)
+{
+ NTSTATUS Status;
+ UNICODE_STRING FileNameString;
+ RTL_PATH_TYPE PathType;
+
+ /* Build the string */
+ Status = RtlInitUnicodeStringEx(&FileNameString, FileName);
+ if (!NT_SUCCESS(Status)) return 0;
+
+ /* Call the extended function */
+ return RtlGetFullPathName_Ustr(&FileNameString,
+ Size,
+ Buffer,
+ (PCWSTR*)ShortName,
+ NULL,
+ &PathType);
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ /* Call the internal function */
+ return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE,
+ DosName,
+ NtName,
+ PartName,
+ RelativeName));
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ /* Call the internal function */
+ return RtlpDosPathNameToRelativeNtPathName_U(FALSE,
+ DosName,
+ NtName,
+ PartName,
+ RelativeName);
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ /* Call the internal function */
+ ASSERT(RelativeName);
+ return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE,
+ DosName,
+ NtName,
+ PartName,
+ RelativeName));
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,
+ OUT PUNICODE_STRING NtName,
+ OUT PCWSTR *PartName,
+ OUT PRTL_RELATIVE_NAME_U RelativeName)
+{
+ /* Call the internal function */
+ ASSERT(RelativeName);
+ return RtlpDosPathNameToRelativeNtPathName_U(TRUE,
+ DosName,
+ NtName,
+ PartName,
+ RelativeName);
+}
+
+/*
+ * @unimplemented
+ */
+NTSTATUS NTAPI
+RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
+{
+ DPRINT1("RtlNtPathNameToDosPathName: stub\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlDosSearchPath_U(IN PCWSTR Path,
+ IN PCWSTR FileName,
+ IN PCWSTR Extension,
+ IN ULONG Size,
+ IN PWSTR Buffer,
+ OUT PWSTR *PartName)
+{
+ NTSTATUS Status;
+ ULONG ExtensionLength, Length, FileNameLength, PathLength;
+ UNICODE_STRING TempString;
+ PWCHAR NewBuffer, BufferStart;
+ PCWSTR p;
+
+ /* Check if this is an absolute path */
+ if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
+ {
+ /* Check if the file exists */
+ if (RtlDoesFileExists_UEx(FileName, TRUE))
+ {
+ /* Get the full name, which does the DOS lookup */
+ return RtlGetFullPathName_U(FileName, Size, Buffer, PartName);
+ }
+
+ /* Doesn't exist, so fail */
+ return 0;
+ }
+
+ /* Scan the filename */
+ p = FileName;
+ while (*p)
+ {
+ /* Looking for an extension */
+ if (*p == L'.')
+ {
+ /* No extension string needed -- it's part of the filename */
+ Extension = NULL;
+ break;
+ }
+
+ /* Next character */
+ p++;
+ }
+
+ /* Do we have an extension? */
+ if (!Extension)
+ {
+ /* Nope, don't worry about one */
+ ExtensionLength = 0;
+ }
+ else
+ {
+ /* Build a temporary string to get the extension length */
+ Status = RtlInitUnicodeStringEx(&TempString, Extension);
+ if (!NT_SUCCESS(Status)) return 0;
+ ExtensionLength = TempString.Length;
+ }
+
+ /* Build a temporary string to get the path length */
+ Status = RtlInitUnicodeStringEx(&TempString, Path);
+ if (!NT_SUCCESS(Status)) return 0;
+ PathLength = TempString.Length;
+
+ /* Build a temporary string to get the filename length */
+ Status = RtlInitUnicodeStringEx(&TempString, FileName);
+ if (!NT_SUCCESS(Status)) return 0;
+ FileNameLength = TempString.Length;
+
+ /* Allocate the buffer for the new string name */
+ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ FileNameLength +
+ ExtensionLength +
+ PathLength +
+ 3 * sizeof(WCHAR));
+ if (!NewBuffer)
+ {
+ /* Fail the call */
+ DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ /* Final loop to build the path */
+ while (TRUE)
+ {
+ /* Check if we have a valid character */
+ BufferStart = NewBuffer;
+ if (*Path)
+ {
+ /* Loop as long as there's no semicolon */
+ while (*Path != L';')
+ {
+ /* Copy the next character */
+ *BufferStart++ = *Path++;
+ if (!*Path) break;
+ }
+
+ /* We found a semi-colon, to stop path processing on this loop */
+ if (*Path == L';') ++Path;
+ }
+
+ /* Add a terminating slash if needed */
+ if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR))
+ {
+ *BufferStart++ = OBJ_NAME_PATH_SEPARATOR;
+ }
+
+ /* Bail out if we reached the end */
+ if (!*Path) Path = NULL;
+
+ /* Copy the file name and check if an extension is needed */
+ RtlCopyMemory(BufferStart, FileName, FileNameLength);
+ if (ExtensionLength)
+ {
+ /* Copy the extension too */
+ RtlCopyMemory((PCHAR)BufferStart + FileNameLength,
+ Extension,
+ ExtensionLength + sizeof(WCHAR));
+ }
+ else
+ {
+ /* Just NULL-terminate */
+ *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL;
+ }
+
+ /* Now, does this file exist? */
+ if (RtlDoesFileExists_UEx(NewBuffer, FALSE))
+ {
+ /* Call the full-path API to get the length */
+ Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName);
+ break;
+ }
+
+ /* If we got here, path doesn't exist, so fail the call */
+ Length = 0;
+ if (!Path) break;
+ }
+
+ /* Free the allocation and return the length */
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
+ return Length;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,
+ IN PUNICODE_STRING StaticString,
+ IN PUNICODE_STRING DynamicString,
+ IN PUNICODE_STRING *StringUsed,
+ IN PSIZE_T FilePartSize,
+ OUT PBOOLEAN NameInvalid,
+ OUT RTL_PATH_TYPE* PathType,
+ OUT PSIZE_T LengthNeeded)
+{
+ NTSTATUS Status;
+ PWCHAR StaticBuffer;
+ PCWCH ShortName;
+ ULONG Length;
+ USHORT StaticLength;
+ UNICODE_STRING TempDynamicString;
+
+ /* Initialize all our locals */
+ ShortName = NULL;
+ StaticBuffer = NULL;
+ TempDynamicString.Buffer = NULL;
+
+ /* Initialize the input parameters */
+ if (StringUsed) *StringUsed = NULL;
+ if (LengthNeeded) *LengthNeeded = 0;
+ if (FilePartSize) *FilePartSize = 0;
+
+ /* Check for invalid parameters */
+ if ((DynamicString) && !(StringUsed) && (StaticString))
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Check if we did not get an input string */
+ if (!StaticString)
+ {
+ /* Allocate one */
+ StaticLength = MAX_PATH * sizeof(WCHAR);
+ StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR);
+ if (!StaticBuffer) return STATUS_NO_MEMORY;
+ }
+ else
+ {
+ /* Use the one we received */
+ StaticBuffer = StaticString->Buffer;
+ StaticLength = StaticString->MaximumLength;
+ }
+
+ /* Call the lower-level function */
+ Length = RtlGetFullPathName_Ustr(FileName,
+ StaticLength,
+ StaticBuffer,
+ &ShortName,
+ NameInvalid,
+ PathType);
+ DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer);
+ if (!Length)
+ {
+ /* Fail if it failed */
+ DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
+ __FUNCTION__,
+ __LINE__);
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Quickie;
+ }
+
+ /* Check if it fits inside our static string */
+ if ((StaticString) && (Length < StaticLength))
+ {
+ /* Set the final length */
+ StaticString->Length = (USHORT)Length;
+
+ /* Set the file part size */
+ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
+
+ /* Return the static string if requested */
+ if (StringUsed) *StringUsed = StaticString;
+
+ /* We are done with success */
+ Status = STATUS_SUCCESS;
+ goto Quickie;
+ }
+
+ /* Did we not have an input dynamic string ?*/
+ if (!DynamicString)
+ {
+ /* Return the length we need */
+ if (LengthNeeded) *LengthNeeded = Length;
+
+ /* And fail such that the caller can try again */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Quickie;
+ }
+
+ /* Check if it fits in our static buffer */
+ if ((StaticBuffer) && (Length < StaticLength))
+ {
+ /* NULL-terminate it */
+ StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Set the settings for the dynamic string the caller sent */
+ DynamicString->MaximumLength = StaticLength;
+ DynamicString->Length = (USHORT)Length;
+ DynamicString->Buffer = StaticBuffer;
+
+ /* Set the part size */
+ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0;
+
+ /* Return the dynamic string if requested */
+ if (StringUsed) *StringUsed = DynamicString;
+
+ /* Do not free the static buffer on exit, and return success */
+ StaticBuffer = NULL;
+ Status = STATUS_SUCCESS;
+ goto Quickie;
+ }
+
+ /* Now try again under the PEB lock */
+ RtlAcquirePebLock();
+ Length = RtlGetFullPathName_Ustr(FileName,
+ StaticLength,
+ StaticBuffer,
+ &ShortName,
+ NameInvalid,
+ PathType);
+ if (!Length)
+ {
+ /* It failed */
+ DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
+ __FUNCTION__, __LINE__);
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Release;
+ }
+
+ /* Check if it fits inside our static string now */
+ if ((StaticString) && (Length < StaticLength))
+ {
+ /* Set the final length */
+ StaticString->Length = (USHORT)Length;
+
+ /* Set the file part size */
+ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
+
+ /* Return the static string if requested */
+ if (StringUsed) *StringUsed = StaticString;
+
+ /* We are done with success */
+ Status = STATUS_SUCCESS;
+ goto Release;
+ }
+
+ /* Check if the path won't even fit in a real string */
+ if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
+ {
+ /* Name is way too long, fail */
+ Status = STATUS_NAME_TOO_LONG;
+ goto Release;
+ }
+
+ /* Allocate the string to hold the path name now */
+ TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR),
+ TAG_USTR);
+ if (!TempDynamicString.Buffer)
+ {
+ /* Out of memory, fail */
+ Status = STATUS_NO_MEMORY;
+ goto Release;
+ }
+
+ /* Add space for a NULL terminator, and now check the full path */
+ TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
+ Length = RtlGetFullPathName_Ustr(FileName,
+ Length,
+ TempDynamicString.Buffer,
+ &ShortName,
+ NameInvalid,
+ PathType);
+ if (!Length)
+ {
+ /* Some path error, so fail out */
+ DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
+ __FUNCTION__, __LINE__);
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Release;
+ }
+
+ /* It should fit in the string we just allocated */
+ ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR)));
+ if (Length > TempDynamicString.MaximumLength)
+ {
+ /* This is really weird and would mean some kind of race */
+ Status = STATUS_INTERNAL_ERROR;
+ goto Release;
+ }
+
+ /* Return the file part size */
+ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0;
+
+ /* Terminate the whole string now */
+ TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Finalize the string and return it to the user */
+ DynamicString->Buffer = TempDynamicString.Buffer;
+ DynamicString->Length = (USHORT)Length;
+ DynamicString->MaximumLength = TempDynamicString.MaximumLength;
+ if (StringUsed) *StringUsed = DynamicString;
+
+ /* Return success and make sure we don't free the buffer on exit */
+ TempDynamicString.Buffer = NULL;
+ Status = STATUS_SUCCESS;
+
+Release:
+ /* Release the PEB lock */
+ RtlReleasePebLock();
+
+Quickie:
+ /* Free any buffers we should be freeing */
+ DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer);
+ if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer))
+ {
+ RtlpFreeMemory(StaticBuffer, TAG_USTR);
+ }
+ if (TempDynamicString.Buffer)
+ {
+ RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR);
+ }
+
+ /* Print out any unusual errors */
+ if ((NT_ERROR(Status)) &&
+ (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
+ {
+ DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
+ __FUNCTION__, FileName, Status);
+ }
+
+ /* Return, we're all done */
+ return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlDosSearchPath_Ustr(IN ULONG Flags,
+ IN PUNICODE_STRING PathString,
+ IN PUNICODE_STRING FileNameString,
+ IN PUNICODE_STRING ExtensionString,
+ IN PUNICODE_STRING CallerBuffer,
+ IN OUT PUNICODE_STRING DynamicString OPTIONAL,
+ OUT PUNICODE_STRING* FullNameOut OPTIONAL,
+ OUT PSIZE_T FilePartSize OPTIONAL,
+ OUT PSIZE_T LengthNeeded OPTIONAL)
+{
+ WCHAR StaticCandidateBuffer[MAX_PATH];
+ UNICODE_STRING StaticCandidateString;
+ NTSTATUS Status;
+ RTL_PATH_TYPE PathType;
+ PWCHAR p, End, CandidateEnd, SegmentEnd;
+ SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0;
+ USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0;
+ PUNICODE_STRING FullIsolatedPath;
+ DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
+ Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString);
+
+ /* Initialize the input string */
+ RtlInitEmptyUnicodeString(&StaticCandidateString,
+ StaticCandidateBuffer,
+ sizeof(StaticCandidateBuffer));
+
+ /* Initialize optional arguments */
+ if (FullNameOut ) *FullNameOut = NULL;
+ if (FilePartSize) *FilePartSize = 0;
+ if (LengthNeeded) *LengthNeeded = 0;
+ if (DynamicString)
+ {
+ DynamicString->Length = DynamicString->MaximumLength = 0;
+ DynamicString->Buffer = NULL;
+ }
+
+ /* Check for invalid parameters */
+ if ((Flags & ~7) ||
+ !(PathString) ||
+ !(FileNameString) ||
+ ((CallerBuffer) && (DynamicString) && !(FullNameOut)))
+ {
+ /* Fail */
+ DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__);
+ Status = STATUS_INVALID_PARAMETER;
+ goto Quickie;
+ }
+
+ /* First check what kind of path this is */
+ PathType = RtlDetermineDosPathNameType_Ustr(FileNameString);
+
+ /* Check if the caller wants to prevent relative .\ and ..\ paths */
+ if ((Flags & 2) &&
+ (PathType == RtlPathTypeRelative) &&
+ (FileNameString->Length >= (2 * sizeof(WCHAR))) &&
+ (FileNameString->Buffer[0] == L'.') &&
+ ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) ||
+ ((FileNameString->Buffer[1] == L'.') &&
+ ((FileNameString->Length >= (3 * sizeof(WCHAR))) &&
+ (IS_PATH_SEPARATOR(FileNameString->Buffer[2]))))))
+ {
+ /* Yes, and this path is like that, so make it seem unknown */
+ PathType = RtlPathTypeUnknown;
+ }
+
+ /* Now check relative vs non-relative paths */
+ if (PathType == RtlPathTypeRelative)
+ {
+ /* Does the caller want SxS? */
+ if (Flags & 1)
+ {
+ /* Apply the SxS magic */
+ FullIsolatedPath = NULL;
+ Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
+ FileNameString,
+ ExtensionString,
+ CallerBuffer,
+ DynamicString,
+ &FullIsolatedPath,
+ NULL,
+ FilePartSize,
+ LengthNeeded);
+ if (NT_SUCCESS(Status))
+ {
+ /* We found the SxS path, return it */
+ if (FullNameOut) *FullNameOut = FullIsolatedPath;
+ goto Quickie;
+ }
+ else if (Status != STATUS_SXS_KEY_NOT_FOUND)
+ {
+ /* Critical SxS error, fail */
+ DbgPrint("%s: Failing because call to "
+ "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
+ "status 0x%08lx\n",
+ __FUNCTION__,
+ FileNameString,
+ Status);
+ goto Quickie;
+ }
+ }
+
+ /* No SxS key found, or not requested, check if there's an extension */
+ if (ExtensionString)
+ {
+ /* Save the extension length, and check if there's a file name */
+ ExtensionLength = ExtensionString->Length;
+ if (FileNameString->Length)
+ {
+ /* Start parsing the file name */
+ End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)];
+ while (End > FileNameString->Buffer)
+ {
+ /* If we find a path separator, there's no extension */
+ if (IS_PATH_SEPARATOR(*--End)) break;
+
+ /* Otherwise, did we find an extension dot? */
+ if (*End == L'.')
+ {
+ /* Ignore what the caller sent it, use the filename's */
+ ExtensionString = NULL;
+ ExtensionLength = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Check if we got a path */
+ if (PathString->Length)
+ {
+ /* Start parsing the path name, looking for path separators */
+ End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)];
+ p = End;
+ while ((p > PathString->Buffer) && (*--p == L';'))
+ {
+ /* This is the size of the path -- handle a trailing slash */
+ PathSize = End - p - 1;
+ if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
+
+ /* Check if we found a bigger path than before */
+ if (PathSize > MaxPathSize) MaxPathSize = PathSize;
+
+ /* Keep going with the path after this path separator */
+ End = p;
+ }
+
+ /* This is the trailing path, run the same code as above */
+ PathSize = End - p;
+ if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
+ if (PathSize > MaxPathSize) MaxPathSize = PathSize;
+
+ /* Finally, convert the largest path size into WCHAR */
+ MaxPathSize *= sizeof(WCHAR);
+ }
+
+ /* Use the extension, the file name, and the largest path as the size */
+ WorstCaseLength = ExtensionLength +
+ FileNameString->Length +
+ (USHORT)MaxPathSize +
+ sizeof(UNICODE_NULL);
+ if (WorstCaseLength > UNICODE_STRING_MAX_BYTES)
+ {
+ /* It has to fit in a registry string, if not, fail here */
+ DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
+ "worst case file name length is %Iu bytes\n",
+ __FUNCTION__,
+ WorstCaseLength);
+ Status = STATUS_NAME_TOO_LONG;
+ goto Quickie;
+ }
+
+ /* Scan the path now, to see if we can find the file */
+ p = PathString->Buffer;
+ End = &p[PathString->Length / sizeof(WCHAR)];
+ while (p < End)
+ {
+ /* Find out where this path ends */
+ for (SegmentEnd = p;
+ ((SegmentEnd != End) && (*SegmentEnd != L';'));
+ SegmentEnd++);
+
+ /* Compute the size of this path */
+ ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR);
+
+ /* Handle trailing slash if there isn't one */
+ if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1))))
+ {
+ /* Add space for one */
+ SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR);
+ }
+
+ /* Now check if our initial static buffer is too small */
+ if (StaticCandidateString.MaximumLength <
+ (SegmentSize + ExtensionLength + FileNameString->Length))
+ {
+ /* At this point we should've been using our static buffer */
+ ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer);
+ if (StaticCandidateString.Buffer != StaticCandidateBuffer)
+ {
+ /* Something is really messed up if this was the dynamic string */
+ DbgPrint("%s: internal error #1; "
+ "CandidateString.Buffer = %p; "
+ "StaticCandidateBuffer = %p\n",
+ __FUNCTION__,
+ StaticCandidateString.Buffer,
+ StaticCandidateBuffer);
+ Status = STATUS_INTERNAL_ERROR;
+ goto Quickie;
+ }
+
+ /* We checked before that the maximum possible size shoudl fit! */
+ ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) <
+ UNICODE_STRING_MAX_BYTES);
+ if ((SegmentSize + ExtensionLength + FileNameString->Length) >
+ (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR)))
+ {
+ /* For some reason it's not fitting anymore. Something messed up */
+ DbgPrint("%s: internal error #2; SegmentSize = %u, "
+ "FileName->Length = %u, DefaultExtensionLength = %u\n",
+ __FUNCTION__,
+ SegmentSize,
+ FileNameString->Length,
+ ExtensionLength);
+ Status = STATUS_INTERNAL_ERROR;
+ goto Quickie;
+ }
+
+ /* Now allocate the dynamic string */
+ StaticCandidateString.MaximumLength = FileNameString->Length +
+ WorstCaseLength;
+ StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength,
+ TAG_USTR);
+ if (!StaticCandidateString.Buffer)
+ {
+ /* Out of memory, fail */
+ DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
+ __FUNCTION__,
+ StaticCandidateString.MaximumLength);
+ Status = STATUS_NO_MEMORY;
+ goto Quickie;
+ }
+ }
+
+ /* Copy the path in the string */
+ RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount);
+
+ /* Get to the end of the string, and add the trailing slash if missing */
+ CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)];
+ if ((SegmentSize) && (SegmentSize != ByteCount))
+ {
+ *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR;
+ }
+
+ /* Copy the filename now */
+ RtlCopyMemory(CandidateEnd,
+ FileNameString->Buffer,
+ FileNameString->Length);
+ CandidateEnd += (FileNameString->Length / sizeof(WCHAR));
+
+ /* Check if there was an extension */
+ if (ExtensionString)
+ {
+ /* Copy the extension too */
+ RtlCopyMemory(CandidateEnd,
+ ExtensionString->Buffer,
+ ExtensionString->Length);
+ CandidateEnd += (ExtensionString->Length / sizeof(WCHAR));
+ }
+
+ /* We are done, terminate it */
+ *CandidateEnd = UNICODE_NULL;
+
+ /* Now set the final length of the string so it becomes valid */
+ StaticCandidateString.Length = (USHORT)(CandidateEnd -
+ StaticCandidateString.Buffer) *
+ sizeof(WCHAR);
+
+ /* Check if this file exists */
+ DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer);
+ if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE))
+ {
+ /* Awesome, it does, now get the full path */
+ Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
+ CallerBuffer,
+ DynamicString,
+ (PUNICODE_STRING*)FullNameOut,
+ FilePartSize,
+ NULL,
+ &PathType,
+ LengthNeeded);
+ if (!(NT_SUCCESS(Status)) &&
+ ((Status != STATUS_NO_SUCH_FILE) &&
+ (Status != STATUS_BUFFER_TOO_SMALL)))
+ {
+ DbgPrint("%s: Failing because we thought we found %wZ on "
+ "the search path, but RtlGetfullPathNameUStrEx() "
+ "returned %08lx\n",
+ __FUNCTION__,
+ &StaticCandidateString,
+ Status);
+ }
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ goto Quickie;
+ }
+ else
+ {
+ /* Otherwise, move to the next path */
+ if (SegmentEnd != End)
+ {
+ /* Handle the case of the path separator trailing */
+ p = SegmentEnd + 1;
+ }
+ else
+ {
+ p = SegmentEnd;
+ }
+ }
+ }
+
+ /* Loop finished and we didn't break out -- fail */
+ Status = STATUS_NO_SUCH_FILE;
+ }
+ else
+ {
+ /* We have a full path, so check if it does exist */
+ DPRINT("%wZ\n", FileNameString);
+ if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE))
+ {
+ /* It doesn't exist, did we have an extension? */
+ if (!(ExtensionString) || !(ExtensionString->Length))
+ {
+ /* No extension, so just fail */
+ Status = STATUS_NO_SUCH_FILE;
+ goto Quickie;
+ }
+
+ /* There was an extension, check if the filename already had one */
+ if (!(Flags & 4) && (FileNameString->Length))
+ {
+ /* Parse the filename */
+ p = FileNameString->Buffer;
+ End = &p[FileNameString->Length / sizeof(WCHAR)];
+ while (End > p)
+ {
+ /* If there's a path separator, there's no extension */
+ if (IS_PATH_SEPARATOR(*--End)) break;
+
+ /* Othwerwise, did we find an extension dot? */
+ if (*End == L'.')
+ {
+ /* File already had an extension, so fail */
+ Status = STATUS_NO_SUCH_FILE;
+ goto Quickie;
+ }
+ }
+ }
+
+ /* So there is an extension, we'll try again by adding it */
+ NamePlusExtLength = FileNameString->Length +
+ ExtensionString->Length +
+ sizeof(UNICODE_NULL);
+ if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES)
+ {
+ /* It won't fit in any kind of valid string, so fail */
+ DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
+ __FUNCTION__,
+ NamePlusExtLength);
+ Status = STATUS_NAME_TOO_LONG;
+ goto Quickie;
+ }
+
+ /* Fill it fit in our temporary string? */
+ if (NamePlusExtLength > StaticCandidateString.MaximumLength)
+ {
+ /* It won't fit anymore, allocate a dynamic string for it */
+ StaticCandidateString.MaximumLength = NamePlusExtLength;
+ StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength,
+ TAG_USTR);
+ if (!StaticCandidateString.Buffer)
+ {
+ /* Ran out of memory, so fail */
+ DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
+ __FUNCTION__);
+ Status = STATUS_NO_MEMORY;
+ goto Quickie;
+ }
+ }
+
+ /* Copy the filename */
+ RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
+
+ /* Copy the extension */
+ RtlAppendUnicodeStringToString(&StaticCandidateString,
+ ExtensionString);
+
+ DPRINT("SB: %wZ\n", &StaticCandidateString);
+
+ /* And check if this file now exists */
+ if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE))
+ {
+ /* Still no joy, fail out */
+ Status = STATUS_NO_SUCH_FILE;
+ goto Quickie;
+ }
+
+ /* File was found, get the final full path */
+ Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
+ CallerBuffer,
+ DynamicString,
+ (PUNICODE_STRING*)FullNameOut,
+ FilePartSize,
+ NULL,
+ &PathType,
+ LengthNeeded);
+ if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE))
+ {
+ DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
+ "failed with status %08lx\n",
+ __FUNCTION__,
+ &StaticCandidateString,
+ Status);
+ }
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ }
+ else
+ {
+ /* File was found on the first try, get the final full path */
+ Status = RtlGetFullPathName_UstrEx(FileNameString,
+ CallerBuffer,
+ DynamicString,
+ (PUNICODE_STRING*)FullNameOut,
+ FilePartSize,
+ NULL,
+ &PathType,
+ LengthNeeded);
+ if (!(NT_SUCCESS(Status)) &&
+ ((Status != STATUS_NO_SUCH_FILE) &&
+ (Status != STATUS_BUFFER_TOO_SMALL)))
+ {
+ DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
+ "failed with status %08lx\n",
+ __FUNCTION__,
+ FileNameString,
+ Status);
+ }
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ }
+ }
+
+Quickie:
+ /* Anything that was not an error, turn into STATUS_SUCCESS */
+ if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS;
+
+ /* Check if we had a dynamic string */
+ if ((StaticCandidateString.Buffer) &&
+ (StaticCandidateString.Buffer != StaticCandidateBuffer))
+ {
+ /* Free it */
+ RtlFreeUnicodeString(&StaticCandidateString);
+ }
+
+ /* Return the status */
+ return Status;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+RtlDoesFileExists_U(IN PCWSTR FileName)
+{
+ /* Call the new function */
+ return RtlDoesFileExists_UEx(FileName, TRUE);
}
/* EOF */