* 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 ********************************************************************/
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 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 RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
+PRTLP_CURDIR_REF RtlpCurDirRef;
+
/* PRIVATE FUNCTIONS **********************************************************/
+RTL_PATH_TYPE
+NTAPI
+RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
+{
+ 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 (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 */
+ if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
+ if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
+ return RtlPathTypeRootLocalDevice; /* \\. or \\? */
+ }
+ else
+ {
+ if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
+ if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */
+ return RtlPathTypeDriveAbsolute; /* x:\ */
+ }
+}
+
ULONG
NTAPI
-RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
+RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
{
UNICODE_STRING PathCopy;
PWCHAR Start, End;
{
/* Stop if we hit something else than a space or period */
c = PathCopy.Buffer[PathChars - 1];
- if ((c != '.') && (c != ' ')) break;
+ if ((c != L'.') && (c != L' ')) break;
/* Fixup the lengths */
PathCopy.Length -= sizeof(WCHAR);
{
/* 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[OriginalLength / sizeof(WCHAR)]) &&
- ((c == 'l') || (c == 'c') || (c == 'p') || (c == 'a') || (c == 'n')))
+ ((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 = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
}
/* 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);
return 0;
}
-RTL_PATH_TYPE
-NTAPI
-RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
-{
- PWCHAR Path;
- ULONG Chars;
-
- /* Validate the input */
- if (!PathString) return RtlPathTypeUnknown;
-
- Path = PathString->Buffer;
- Chars = PathString->Length / sizeof(WCHAR);
-
- /* Return if there are no characters */
- if (!Chars) return RtlPathTypeUnknown;
-
- /*
- * 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 ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */
- if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */
- if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
- if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
- return RtlPathTypeRootLocalDevice; /* \\. or \\? */
- }
- 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: */
- }
-}
-
NTSTATUS
NTAPI
RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
return Status;
}
+
+
+/******************************************************************
+ * RtlpCollapsePath (from WINE)
+ *
+ * Helper for RtlGetFullPathName_U.
+ * 1) Convert slashes into backslashes
+ * 2) Get rid of duplicate backslashes
+ * 3) Get rid of . and .. components in the path.
+ */
+static VOID
+RtlpCollapsePath(PWSTR path, ULONG mark, BOOLEAN SkipTrailingPathSeparators)
+{
+ PWSTR p, next;
+
+ /* convert every / into a \ */
+ 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 != L'\\' || next[-1] != L'\\') *next++ = *p;
+ }
+ *next = UNICODE_NULL;
+
+ p = path + mark;
+ while (*p)
+ {
+ if (*p == L'.')
+ {
+ switch(p[1])
+ {
+ 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 */
+ {
+ 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;
+ }
+ else if (!p[2]) /* final .. */
+ {
+ if (p > path + mark)
+ {
+ p--;
+ while (p > path + mark && p[-1] != L'\\') p--;
+ if (p > path + mark) p--;
+ }
+ *p = UNICODE_NULL;
+ continue;
+ }
+ break;
+ }
+ }
+ /* skip to the next component */
+ while (*p && *p != L'\\') p++;
+ if (*p == L'\\')
+ {
+ /* remove last dot in previous dir name */
+ if (p > path + mark && p[-1] == L'.')
+ RtlMoveMemory( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
+ else
+ p++;
+ }
+ }
+
+ /* 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;
+
+ /* Null-terminate the string */
+ *p = UNICODE_NULL;
+}
+
+/******************************************************************
+ * RtlpSkipUNCPrefix (from WINE)
+ *
+ * Helper for RtlGetFullPathName_U
+ * Skip the \\share\dir part of a file name and return the new position
+ * (which can point on the last backslash of "dir\")
+ */
+static SIZE_T
+RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
+{
+ PCWSTR UncPath = FileNameBuffer + 2;
+ DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
+
+ 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++; */
+
+ return (UncPath - FileNameBuffer);
+}
+
+NTSTATUS
+NTAPI
+RtlpApplyLengthFunction(IN ULONG Flags,
+ IN ULONG Type,
+ IN PVOID UnicodeStringOrUnicodeStringBuffer,
+ IN PVOID LengthFunction)
+{
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+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;
+}
+
ULONG
NTAPI
-RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName,
- IN ULONG Size,
- IN PWSTR Buffer,
- OUT PCWSTR *ShortName,
- OUT PBOOLEAN InvalidName,
- OUT RTL_PATH_TYPE *PathType)
+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;
- NTSTATUS Status;
+
+
+ ULONG reqsize = 0;
+ PCWSTR ptr;
+
+ PCUNICODE_STRING CurDirName;
+ UNICODE_STRING EnvVarName, EnvVarValue;
+ WCHAR EnvVarNameBuffer[4];
+
+ 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;
+
/* For now, assume the name is valid */
DPRINT("Filename: %wZ\n", FileName);
- DPRINT("Size and buffer: %lx %S\n", Size, Buffer);
+ DPRINT("Size and buffer: %lx %p\n", Size, Buffer);
if (InvalidName) *InvalidName = FALSE;
/* Handle initial path type and failure case */
*PathType = RtlPathTypeUnknown;
- if (!(Size) || !(Buffer) || !(FileName) ||
- !(FileName->Length) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
+ if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
/* Break filename into component parts */
FileNameBuffer = FileName->Buffer;
FileNameLength = FileName->Length;
- FileNameChars = FileNameLength / sizeof(WCHAR);
+ FileNameChars = FileNameLength / sizeof(WCHAR);
/* Kill trailing spaces */
c = FileNameBuffer[FileNameChars - 1];
- while ((FileNameLength) && (c == L' '))
+ while ((FileNameLength != 0) && (c == L' '))
{
/* Keep going, ignoring the spaces */
FileNameLength -= sizeof(WCHAR);
- if (FileNameLength) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
+ if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
}
/* Check if anything is left */
- if (!FileNameLength) return 0;
+ if (FileNameLength == 0) return 0;
+
+ /*
+ * 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]);
/* Check if this is a DOS name */
DosLength = RtlIsDosDeviceName_Ustr(FileName);
DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
- if (DosLength)
+ if (DosLength != 0)
{
/* Zero out the short name */
if (ShortName) *ShortName = NULL;
/* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
- DosLengthOffset = DosLength >> 16;
- DosLength = DosLength & 0xFFFF;
+ DosLengthOffset = HIWORD(DosLength);
+ DosLength = LOWORD(DosLength);
/* Do we have a DOS length, and does the caller want validity? */
- if ((InvalidName) && (DosLengthOffset))
+ if (InvalidName && (DosLengthOffset != 0))
{
/* Do the check */
Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
return FullLength + sizeof(UNICODE_NULL);
}
- /* This should work well enough for our current needs */
+ /* Zero out the destination buffer. FileName must be different from Buffer */
+ RtlZeroMemory(Buffer, Size);
+
+ /* Get the path type */
*PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
- DPRINT("Path type: %lx\n", *PathType);
- /* This is disgusting... but avoids re-writing everything */
- DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer, Size, Buffer);
- return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName);
+
+
+ /**********************************************
+ ** CODE REWRITTEN IS HAPPENING THERE **
+ **********************************************/
+ Source = FileNameBuffer;
+ SourceLength = FileNameLength;
+ EnvVarValue.Buffer = NULL;
+
+ /* Lock the PEB to get the current directory */
+ RtlAcquirePebLock();
+ CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
+
+ switch (*PathType)
+ {
+ case RtlPathTypeUncAbsolute: /* \\foo */
+ PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer);
+ break;
+
+ case RtlPathTypeLocalDevice: /* \\.\foo */
+ PrefixCut = 4;
+ break;
+
+ case RtlPathTypeDriveAbsolute: /* c:\foo */
+ ASSERT(FileNameBuffer[1] == L':');
+ ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
+
+ Prefix = FileNameBuffer;
+ PrefixLength = 3 * sizeof(WCHAR);
+ Source += 3;
+ SourceLength -= 3 * sizeof(WCHAR);
+
+ PrefixCut = 3;
+ break;
+
+ case RtlPathTypeDriveRelative: /* c:foo */
+ Source += 2;
+ SourceLength -= 2 * sizeof(WCHAR);
+ if (RtlUpcaseUnicodeChar(FileNameBuffer[0]) != RtlUpcaseUnicodeChar(CurDirName->Buffer[0]) ||
+ CurDirName->Buffer[1] != L':')
+ {
+ 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)
+ {
+ 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;
+ }
+ 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;
+ }
+ 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
if (PartName)
{
/* Loop from the back until we find a path separator */
- p = &NewBuffer[(DosLength - 1) / sizeof (WCHAR)];
- while (p > NewBuffer) if (*p-- == '\\') break;
-
- /* Was one found? */
- if (p > NewBuffer)
+ p = &NewBuffer[DosLength / sizeof(WCHAR)];
+ while (--p > NewBuffer)
{
- /* Move past it -- anything left? */
- p++;
- if (!*p)
- {
- /* The path ends with a path separator, no part name */
- *PartName = NULL;
- }
- else
+ /* We found a path separator, move past it */
+ if (*p == OBJ_NAME_PATH_SEPARATOR)
{
- /* What follows the path separator is the part name */
- *PartName = p;
+ ++p;
+ break;
}
}
+
+ /* 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->Length = (USHORT)DosLength;
NtPath->Buffer = NewBuffer;
+ NtPath->Length = (USHORT)DosLength;
NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
return STATUS_SUCCESS;
}
WCHAR BigBuffer[MAX_PATH + 1];
PWCHAR PrefixBuffer, NewBuffer, Buffer;
ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
- UNICODE_STRING CapturedDosName, PartNameString;
+ 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: %wZ, PartName: %p, RelativeName: %p\n",
+ DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
HaveRelative, DosName, NtName, PartName, RelativeName);
MaxLength = sizeof(BigBuffer);
/* Capture input string */
CapturedDosName = *DosName;
- /* Check for \\?\\ form */
+ /* 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;
/* Allocate a buffer to hold the path */
NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
- DPRINT("Length: %lx\n", 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;
/* Nothing else is expected */
default:
ASSERT(FALSE);
-
}
/* Now copy the prefix and the buffer */
RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
- &Buffer[PrefixCut],
+ Buffer + PrefixCut,
PathLength - (PrefixCut * sizeof(WCHAR)));
/* Compute the length */
- Length = PathLength - PrefixCut * sizeof(WCHAR) + PrefixLength;
+ Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR);
LengthChars = Length / sizeof(WCHAR);
/* Setup the actual NT path string and terminate it */
NtName->Length = (USHORT)Length;
NtName->MaximumLength = (USHORT)MaxLength;
NewBuffer[LengthChars] = UNICODE_NULL;
- DPRINT("new buffer: %S\n", NewBuffer);
+ DPRINT("New buffer: %S\n", NewBuffer);
DPRINT("NT Name: %wZ\n", NtName);
/* Check if a partial name was requested */
/* Setup the structure */
RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
RelativeName->ContainingDirectory = NULL;
- RelativeName->CurDirRef = 0;
+ RelativeName->CurDirRef = NULL;
/* Check if the input path itself was relative */
if (InputPathType == RtlPathTypeRelative)
{
- /* FIXME: HACK: Old code */
- PCURDIR cd;
- UNICODE_STRING us;
- cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
- if (cd->Handle)
+ /* Get current directory */
+ CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
+ if (CurrentDirectory->Handle)
{
- RtlInitUnicodeString(&us, Buffer);
- us.Length = (cd->DosPath.Length < us.Length) ? cd->DosPath.Length : us.Length;
- if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
+ 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))
{
- Length = ((cd->DosPath.Length / sizeof(WCHAR)) - PrefixCut) + ((InputPathType == 1) ? 8 : 4);
- RelativeName->RelativeName.Buffer = NewBuffer + Length;
- RelativeName->RelativeName.Length = NtName->Length - (USHORT)(Length * sizeof(WCHAR));
+ /* 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;
- RelativeName->ContainingDirectory = cd->Handle;
+ 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;
}
}
}
NTSTATUS Status;
FILE_BASIC_INFORMATION BasicInformation;
- /* Validate the input */
- if (!FileName) return FALSE;
-
/* Get the NT Path */
Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
&NtPathName,
/* Check if a directory reference was grabbed */
if (RelativeName->CurDirRef)
{
- /* FIXME: Not yet supported */
- UNIMPLEMENTED;
+ /* 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;
}
}
* 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(ANSI_NULL));
+ 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").
*/
-ULONG
+NTSTATUS
+NTAPI
+RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags,
+ IN PCUNICODE_STRING PathString,
+ OUT PULONG Length)
+{
+ 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;
+}
+
+/*
+ * @implemented
+ */
+RTL_PATH_TYPE
NTAPI
RtlDetermineDosPathNameType_U(IN PCWSTR Path)
{
DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
- /* Validate the input */
- if (!Path) return RtlPathTypeUnknown;
-
/* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
if (IS_PATH_SEPARATOR(Path[0]))
{
*/
ULONG
NTAPI
-RtlIsDosDeviceName_U(IN PWSTR Path)
+RtlIsDosDeviceName_U(IN PCWSTR Path)
{
UNICODE_STRING PathString;
NTSTATUS Status;
*/
ULONG
NTAPI
-RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
- IN PWSTR Buffer)
+RtlGetCurrentDirectory_U(
+ _In_ ULONG MaximumLength,
+ _Out_bytecap_(MaximumLength) PWSTR Buffer)
{
ULONG Length, Bytes;
PCURDIR CurDir;
/*
* @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;
- USHORT 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
- *
- * Helper for RtlGetFullPathName_U.
- * Get rid of . and .. components in the path.
- */
-void FORCEINLINE collapse_path( WCHAR *path, UINT mark )
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
{
- WCHAR *p, *next;
+ 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;
- /* convert every / into a \ */
- for (p = path; *p; p++) if (*p == '/') *p = '\\';
+ DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
- /* collapse duplicate backslashes */
- next = path + max( 1, mark );
- for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
- *next = 0;
+ /* Initialize for failure case */
+ RtlInitEmptyUnicodeString(&NtName, NULL, 0);
- p = path + mark;
- while (*p)
+ /* Can't set current directory on DOS device */
+ if (RtlIsDosDeviceName_Ustr(Path))
{
- if (*p == '.')
- {
- 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)
- {
- p--;
- while (p > path + mark && p[-1] != '\\') p--;
- }
- memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
- continue;
- }
- else if (!p[2]) /* final .. */
- {
- if (p > path + mark)
- {
- p--;
- while (p > path + mark && p[-1] != '\\') p--;
- if (p > path + mark) p--;
- }
- *p = 0;
- continue;
- }
- break;
- }
- }
- /* skip to the next component */
- while (*p && *p != '\\') p++;
- if (*p == '\\')
- {
- /* remove last dot in previous dir name */
- if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
- else p++;
- }
+ return STATUS_NOT_A_DIRECTORY;
}
- /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
- while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
- *p = 0;
-}
+ /* 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;
+ }
-/******************************************************************
- * 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;
-}
+ /* 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;
+ }
-/******************************************************************
- * get_full_path_helper
- *
- * Helper for RtlGetFullPathName_U
- * Note: name and buffer are allowed to point to the same memory spot
- */
-static ULONG get_full_path_helper(
- LPCWSTR name,
- LPWSTR buffer,
- ULONG size)
-{
- SIZE_T reqsize = 0, mark = 0, dep = 0, deplen;
- LPWSTR ins_str = NULL;
- LPCWSTR ptr;
- const UNICODE_STRING* cd;
- WCHAR tmp[4];
+ SavedLength = FullPath.MaximumLength;
+ CharLength = FullPathLength / sizeof(WCHAR);
- /* return error if name only consists of spaces */
- for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
- if (!*ptr) return 0;
+ if (FullPathLength > FullPath.MaximumLength)
+ {
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
+ }
- RtlAcquirePebLock();
+ /* Translate it to NT name */
+ if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
+ {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Leave;
+ }
- //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
- cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath;
+ InitializeObjectAttributes(&ObjectAttributes, &NtName,
+ OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+ NULL, NULL);
- switch (RtlDetermineDosPathNameType_U(name))
+ /* If previous current directory was removable, then check it for dropping */
+ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
{
- case RtlPathTypeUncAbsolute: /* \\foo */
- ptr = skip_unc_prefix( name );
- mark = (ptr - name);
- break;
+ /* Get back normal handle */
+ CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
+ CurDir->Handle = NULL;
- case RtlPathTypeLocalDevice: /* \\.\foo */
- mark = 4;
- break;
+ /* 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;
- case RtlPathTypeDriveAbsolute: /* c:\foo */
- reqsize = sizeof(WCHAR);
- tmp[0] = towupper(name[0]);
- ins_str = tmp;
- dep = 1;
- mark = 3;
- break;
+ /* Get device information */
+ Status = NtQueryVolumeInformationFile(CurDirHandle,
+ &IoStatusBlock,
+ &FileFsDeviceInfo,
+ sizeof(FileFsDeviceInfo),
+ FileFsDeviceInformation);
+ if (!NT_SUCCESS(Status)) goto Leave;
+ }
- 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 = (USHORT)size;
- val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
- if (val.Buffer == NULL)
- {
- reqsize = 0;
- goto done;
- }
+ /* If device is removable, mark handle */
+ if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
+ {
+ CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
+ }
- 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 */
+ FullPath.Length = (USHORT)FullPathLength;
- case RtlPathTypeRelative: /* foo */
- reqsize = cd->Length;
- ins_str = cd->Buffer;
- if (cd->Buffer[1] != ':')
+ /* If full path isn't \ terminated, do it */
+ if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR)
+ {
+ if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
{
- ptr = skip_unc_prefix( cd->Buffer );
- mark = ptr - cd->Buffer;
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
}
- else mark = 3;
- break;
- 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
+ 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)
{
- ptr = skip_unc_prefix( cd->Buffer );
- reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
- mark = reqsize / sizeof(WCHAR);
- ins_str = cd->Buffer;
+ RtlpCurDirRef = OldCurDir;
+ OldCurDir = NULL;
+ Status = STATUS_NO_MEMORY;
+ goto Leave;
}
- break;
- case RtlPathTypeRootLocalDevice: /* \\. */
- reqsize = 4 * sizeof(WCHAR);
- dep = 3;
- tmp[0] = '\\';
- tmp[1] = '\\';
- tmp[2] = '.';
- tmp[3] = '\\';
- ins_str = tmp;
- mark = 4;
- break;
+ /* 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;
- case RtlPathTypeUnknown:
- goto done;
+Leave:
+ RtlReleasePebLock();
+
+ if (FullPath.Buffer)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
}
- /* enough space ? */
- deplen = wcslen(name + dep) * sizeof(WCHAR);
- if (reqsize + deplen + sizeof(WCHAR) > size)
+ if (NtName.Buffer)
{
- /* not enough space, return need size (including terminating '\0') */
- reqsize += deplen + sizeof(WCHAR);
- goto done;
+ RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
}
- memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
- if (reqsize) memcpy(buffer, ins_str, reqsize);
- reqsize += deplen;
+ if (CurDirHandle) NtClose(CurDirHandle);
- if (ins_str != tmp && ins_str != cd->Buffer)
- RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
+ if (OldHandle) NtClose(OldHandle);
- collapse_path( buffer, (ULONG)mark );
- reqsize = wcslen(buffer) * sizeof(WCHAR);
+ if (OldCurDirHandle) NtClose(OldCurDirHandle);
-done:
- RtlReleasePebLock();
- return (ULONG)reqsize;
+ if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
+ {
+ NtClose(OldCurDir->Handle);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
+ }
+
+ return Status;
}
*
* @implemented
*/
-ULONG NTAPI RtlGetFullPathName_U(
- const WCHAR* name,
- ULONG size,
- WCHAR* buffer,
- WCHAR** file_part)
-{
- WCHAR* ptr;
- ULONG dosdev;
- ULONG reqsize;
-
- DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
-
- if (!name || !*name) return 0;
-
- if (file_part) *file_part = NULL;
- /* 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 */
-
- 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;
- }
+/*
+ * @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;
- 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);
- }
+ /* Build the string */
+ Status = RtlInitUnicodeStringEx(&FileNameString, FileName);
+ if (!NT_SUCCESS(Status)) return 0;
- /* find file part */
- if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
- *file_part = ptr;
- return reqsize;
+ /* Call the extended function */
+ return RtlGetFullPathName_Ustr(&FileNameString,
+ Size,
+ Buffer,
+ (PCWSTR*)ShortName,
+ NULL,
+ &PathType);
}
/*
*/
BOOLEAN
NTAPI
-RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName,
+RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,
OUT PUNICODE_STRING NtName,
OUT PCWSTR *PartName,
OUT PRTL_RELATIVE_NAME_U RelativeName)
*/
NTSTATUS
NTAPI
-RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PWSTR DosName,
+RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,
OUT PUNICODE_STRING NtName,
OUT PCWSTR *PartName,
OUT PRTL_RELATIVE_NAME_U RelativeName)
PWCHAR NewBuffer, BufferStart;
PCWSTR p;
- /* Validate the input */
- if (!(Path) || !(FileName)) return 0;
-
/* Check if this is an absolute path */
if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
{
while (*p)
{
/* Looking for an extension */
- if (*p == '.')
+ if (*p == L'.')
{
/* No extension string needed -- it's part of the filename */
Extension = NULL;
if (*Path)
{
/* Loop as long as there's no semicolon */
- while (*Path != ';')
+ while (*Path != L';')
{
/* Copy the next character */
*BufferStart++ = *Path++;
}
/* We found a semi-colon, to stop path processing on this loop */
- if (*Path == ';') ++Path;
+ if (*Path == L';') ++Path;
}
/* Add a terminating slash if needed */
- if ((BufferStart != NewBuffer) && (BufferStart[-1] != '\\'))
+ if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR))
{
- *BufferStart++ = '\\';
+ *BufferStart++ = OBJ_NAME_PATH_SEPARATOR;
}
/* Bail out if we reached the end */
/*
* @implemented
*/
-ULONG
+NTSTATUS
NTAPI
RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,
IN PUNICODE_STRING StaticString,
IN PSIZE_T FilePartSize,
OUT PBOOLEAN NameInvalid,
OUT RTL_PATH_TYPE* PathType,
- OUT PULONG LengthNeeded)
+ OUT PSIZE_T LengthNeeded)
{
NTSTATUS Status;
PWCHAR StaticBuffer;
&ShortName,
NameInvalid,
PathType);
- DPRINT("Length: %d StaticBuffer: %S\n", Length, StaticBuffer);
+ DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer);
if (!Length)
{
/* Fail if it failed */
if ((StaticString) && (Length < StaticLength))
{
/* Set the final length */
- StaticString->Length = Length;
+ StaticString->Length = (USHORT)Length;
/* Set the file part size */
if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
/* Set the settings for the dynamic string the caller sent */
DynamicString->MaximumLength = StaticLength;
- DynamicString->Length = Length;
+ DynamicString->Length = (USHORT)Length;
DynamicString->Buffer = StaticBuffer;
/* Set the part size */
if ((StaticString) && (Length < StaticLength))
{
/* Set the final length */
- StaticString->Length = Length;
+ StaticString->Length = (USHORT)Length;
/* Set the file part size */
if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
}
/* Add space for a NULL terminator, and now check the full path */
- TempDynamicString.MaximumLength = Length + sizeof(UNICODE_NULL);
+ TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
Length = RtlGetFullPathName_Ustr(FileName,
Length,
TempDynamicString.Buffer,
/* Finalize the string and return it to the user */
DynamicString->Buffer = TempDynamicString.Buffer;
- DynamicString->Length = Length;
+ DynamicString->Length = (USHORT)Length;
DynamicString->MaximumLength = TempDynamicString.MaximumLength;
if (StringUsed) *StringUsed = DynamicString;
Quickie:
/* Free any buffers we should be freeing */
DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer);
- if ((StaticBuffer) && (StaticBuffer != StaticString->Buffer))
+ if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer))
{
RtlpFreeMemory(StaticBuffer, TAG_USTR);
}
/*
* @implemented
*/
-ULONG
+NTSTATUS
NTAPI
RtlDosSearchPath_Ustr(IN ULONG Flags,
IN PUNICODE_STRING PathString,
IN PUNICODE_STRING CallerBuffer,
IN OUT PUNICODE_STRING DynamicString OPTIONAL,
OUT PUNICODE_STRING* FullNameOut OPTIONAL,
- OUT PULONG FilePartSize OPTIONAL,
- OUT PULONG LengthNeeded 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;
- ULONG SegmentSize, NamePlusExtLength, PathSize, MaxPathSize = 0, WorstCaseLength, ByteCount;
- USHORT ExtensionLength = 0;
+ 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);
sizeof(StaticCandidateBuffer));
/* Initialize optional arguments */
- if (FullNameOut) *FullNameOut = NULL;
+ if (FullNameOut ) *FullNameOut = NULL;
if (FilePartSize) *FilePartSize = 0;
+ if (LengthNeeded) *LengthNeeded = 0;
if (DynamicString)
{
DynamicString->Length = DynamicString->MaximumLength = 0;
/* Use the extension, the file name, and the largest path as the size */
WorstCaseLength = ExtensionLength +
FileNameString->Length +
- MaxPathSize +
+ (USHORT)MaxPathSize +
sizeof(UNICODE_NULL);
if (WorstCaseLength > UNICODE_STRING_MAX_BYTES)
{
*CandidateEnd = UNICODE_NULL;
/* Now set the final length of the string so it becomes valid */
- StaticCandidateString.Length = (CandidateEnd -
+ StaticCandidateString.Length = (USHORT)(CandidateEnd -
StaticCandidateString.Buffer) *
sizeof(WCHAR);
"the search path, but RtlGetfullPathNameUStrEx() "
"returned %08lx\n",
__FUNCTION__,
+ &StaticCandidateString,
Status);
}
- DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
goto Quickie;
}
else
}
/* Copy the filename */
- RtlCopyMemory(StaticCandidateString.Buffer,
- FileNameString->Buffer,
- FileNameString->Length);
+ RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
/* Copy the extension */
- RtlCopyMemory(&StaticCandidateString.Buffer[FileNameString->Length / sizeof(WCHAR)],
- ExtensionString->Buffer,
- ExtensionString->Length);
-
- /* Now NULL-terminate */
- StaticCandidateString.Buffer[StaticCandidateString.Length / sizeof(WCHAR)] = UNICODE_NULL;
+ RtlAppendUnicodeStringToString(&StaticCandidateString,
+ ExtensionString);
- /* Finalize the length of the string to make it valid */
- StaticCandidateString.Length = FileNameString->Length + ExtensionString->Length;
DPRINT("SB: %wZ\n", &StaticCandidateString);
/* And check if this file now exists */
&StaticCandidateString,
Status);
}
- DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
}
else
{
FileNameString,
Status);
}
- DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+ DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
}
}