* 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 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;
+
+ /* 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: */
+ }
+}
+
ULONG
NTAPI
-RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
+RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
{
- UNICODE_STRING DeviceName;
- PWCHAR Start, End, Ptr;
- ULONG DeviceNameLength;
+ UNICODE_STRING PathCopy;
+ PWCHAR Start, End;
+ USHORT PathChars, ColonCount = 0;
+ USHORT ReturnOffset = 0, ReturnLength, OriginalLength;
+ WCHAR c;
/* Validate the input */
if (!PathString) return 0;
}
return 0;
- /* Skip the drive name for drive relative or absolute paths */
- case RtlPathTypeDriveAbsolute:
- case RtlPathTypeDriveRelative:
- Start = PathString->Buffer + 2;
- break;
-
default:
- Start = PathString->Buffer;
break;
}
- /* Find start of file name */
- for (Ptr = Start; *Ptr; Ptr++)
- if (IS_PATH_SEPARATOR(*Ptr))
- Start = Ptr + 1;
+ /* Make a copy of the string */
+ PathCopy = *PathString;
+ OriginalLength = PathString->Length;
- /* Truncate at extension or stream */
- for (End = Start; *End; End++)
- if (*End == L'.' || *End == L':')
- break;
- End--;
+ /* Return if there's no characters */
+ PathChars = PathCopy.Length / sizeof(WCHAR);
+ if (!PathChars) return 0;
- /* Remove trailing spaces */
- while (End >= Start && *End == L' ')
- End--;
+ /* Check for drive path and truncate */
+ if (PathCopy.Buffer[PathChars - 1] == L':')
+ {
+ /* Fixup the lengths */
+ PathCopy.Length -= sizeof(WCHAR);
+ if (!--PathChars) return 0;
- /* Build the device name string */
- DeviceNameLength = End - Start + 1;
- DeviceName.Buffer = Start;
- DeviceName.Length = (USHORT)DeviceNameLength * sizeof(WCHAR);
- DeviceName.MaximumLength = DeviceName.Length;
+ /* Remember this for later */
+ ColonCount = 1;
+ }
- /* Check the device name */
- if (DeviceNameLength == 3)
+ /* Check for extension or space, and truncate */
+ do
{
- if (RtlPrefixUnicodeString(&RtlpDosAUXDevice, &DeviceName, TRUE) ||
- RtlPrefixUnicodeString(&RtlpDosCONDevice, &DeviceName, TRUE) ||
- RtlPrefixUnicodeString(&RtlpDosNULDevice, &DeviceName, TRUE) ||
- RtlPrefixUnicodeString(&RtlpDosPRNDevice, &DeviceName, TRUE))
+ /* Stop if we hit something else than a space or period */
+ c = PathCopy.Buffer[PathChars - 1];
+ if ((c != '.') && (c != ' ')) break;
+
+ /* Fixup the lengths */
+ PathCopy.Length -= sizeof(WCHAR);
+
+ /* Remember this for later */
+ ColonCount++;
+ } while (--PathChars);
+
+ /* Anything still left? */
+ if (PathChars)
+ {
+ /* Loop from the end */
+ for (End = &PathCopy.Buffer[PathChars - 1];
+ End >= PathCopy.Buffer;
+ --End)
{
- return MAKELONG(DeviceNameLength * sizeof(WCHAR), (Start - PathString->Buffer) * sizeof(WCHAR));
+ /* Check if the character is a path or drive separator */
+ c = *End;
+ if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1)))
+ {
+ /* Get the next lower case character */
+ End++;
+ c = *End | ' '; // ' ' == ('z' - 'Z')
+
+ /* 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')))
+ {
+ /* Calculate the offset */
+ ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
+
+ /* Build the final string */
+ 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;
+ }
+ }
}
- }
- else if (DeviceNameLength == 4)
- {
- if ((RtlPrefixUnicodeString(&RtlpDosCOMDevice, &DeviceName, TRUE) ||
- RtlPrefixUnicodeString(&RtlpDosLPTDevice, &DeviceName, TRUE)) &&
- iswdigit(DeviceName.Buffer[3]) &&
- DeviceName.Buffer[3] != L'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'))
{
- return MAKELONG(DeviceNameLength * sizeof(WCHAR), (Start - PathString->Buffer) * sizeof(WCHAR));
+ /* Not LPT, COM, PRN, AUX, or NUL */
+ return 0;
}
}
- /* Otherwise, this is not a valid DOS device */
- return 0;
-}
-
-RTL_PATH_TYPE
-NTAPI
-RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
-{
- PWCHAR Path;
- ULONG Chars;
-
- /* Validate the input */
- if (!PathString) return RtlPathTypeUnknown;
+ /* Now skip past any extra extension or drive letter characters */
+ Start = PathCopy.Buffer;
+ End = &Start[PathChars];
+ while (Start < End)
+ {
+ c = *Start;
+ if ((c == '.') || (c == ':')) break;
+ Start++;
+ }
- Path = PathString->Buffer;
- Chars = PathString->Length / sizeof(WCHAR);
+ /* And then go backwards to get rid of spaces */
+ while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start;
- /* Return if there are no characters */
- if (!Chars) return RtlPathTypeUnknown;
+ /* Finally see how many characters are left, and that's our size */
+ PathChars = (USHORT)(Start - PathCopy.Buffer);
+ PathCopy.Length = PathChars * sizeof(WCHAR);
- /*
- * 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])))
+ /* 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')))
{
- 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 \\? */
+ /* Don't compare the number part, just check for LPT or COM */
+ PathCopy.Length -= sizeof(WCHAR);
+ if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) ||
+ (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
+ {
+ /* Found it */
+ ReturnLength = sizeof(L"COM1") - sizeof(WCHAR);
+ return MAKELONG(ReturnLength, ReturnOffset);
+ }
}
- else
+ else if ((PathChars == 3) &&
+ ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) ||
+ (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) ||
+ (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) ||
+ (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
{
- 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: */
+ /* Otherwise this was something like AUX, NUL, PRN, or CON */
+ ReturnLength = sizeof(L"AUX") - sizeof(WCHAR);
+ return MAKELONG(ReturnLength, ReturnOffset);
}
+
+ /* Otherwise, this is not a valid DOS device */
+ return 0;
}
NTSTATUS
}
/* Build the final NT path string */
- NtPath->Length = DosLength;
+ NtPath->Length = (USHORT)DosLength;
NtPath->Buffer = NewBuffer;
- NtPath->MaximumLength = DosLength + sizeof(UNICODE_NULL);
+ 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",
/* Setup the actual NT path string and terminate it */
NtName->Buffer = NewBuffer;
- NtName->Length = Length;
- NtName->MaximumLength = MaxLength;
+ NtName->Length = (USHORT)Length;
+ NtName->MaximumLength = (USHORT)MaxLength;
NewBuffer[LengthChars] = UNICODE_NULL;
DPRINT("new buffer: %S\n", NewBuffer);
DPRINT("NT Name: %wZ\n", NtName);
/* 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))
{
- Length = ((cd->DosPath.Length / sizeof(WCHAR)) - PrefixCut) + ((InputPathType == 1) ? 8 : 4);
- RelativeName->RelativeName.Buffer = NewBuffer + Length;
- RelativeName->RelativeName.Length = NtName->Length - (Length * sizeof(WCHAR));
+ 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 + FullPath.Length - PrefixCut);
+ RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
+ /* If relative name starts with \, skip it */
+ if (RelativeName->RelativeName.Buffer[0] == L'\\')
+ {
+ RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)RelativeName->RelativeName.Buffer + sizeof(WCHAR));
+ 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;
}
}
}
/* 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;
}
}
*/
ULONG
NTAPI
-RtlIsDosDeviceName_U(IN PWSTR Path)
+RtlIsDosDeviceName_U(IN PCWSTR Path)
{
UNICODE_STRING PathString;
NTSTATUS Status;
Length = CurDir->DosPath.Length / sizeof(WCHAR);
ASSERT((CurDirName != NULL) && (Length > 0));
- /* Check for x:\ vs x:\path\foo (note the trailing slash) */
+ /*
+ * 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':'))
{
{
/* Call has no space for it, fail, add the trailing slash */
RtlReleasePebLock();
- return Bytes + sizeof(L'\\');
+ return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR);
}
}
else
{
/* Check if caller does not have enough space */
- if (MaximumLength <= Bytes)
+ if (MaximumLength < Bytes)
{
/* Call has no space for it, fail */
RtlReleasePebLock();
RtlCopyMemory(Buffer, CurDirName, Bytes);
/* The buffer should end with a path separator */
- ASSERT(Buffer[Length - 1] == L'\\');
+ 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':'))
/*
* @implemented
*/
-NTSTATUS NTAPI
-RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
{
- 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;
+ 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] != L'\\')
+ {
+ if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
+ {
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
+ }
+
+ FullPath.Buffer[CharLength] = L'\\';
+ 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;
}
LPWSTR buffer,
ULONG size)
{
- ULONG reqsize = 0, mark = 0, dep = 0, deplen;
+ SIZE_T reqsize = 0, mark = 0, dep = 0, deplen;
LPWSTR ins_str = NULL;
LPCWSTR ptr;
const UNICODE_STRING* cd;
var.MaximumLength = 4 * sizeof(WCHAR);
var.Buffer = tmp;
val.Length = 0;
- val.MaximumLength = size;
+ val.MaximumLength = (USHORT)size;
val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
if (val.Buffer == NULL)
{
if (ins_str != tmp && ins_str != cd->Buffer)
RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
- collapse_path( buffer, mark );
+ collapse_path( buffer, (ULONG)mark );
reqsize = wcslen(buffer) * sizeof(WCHAR);
done:
RtlReleasePebLock();
- return reqsize;
+ return (ULONG)reqsize;
}
*/
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)
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: %d 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 ((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
+ */
+ULONG
+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 (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__,
+ 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 */
+ RtlCopyMemory(StaticCandidateString.Buffer,
+ FileNameString->Buffer,
+ FileNameString->Length);
+
+ /* 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;
+
+ /* 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 */
+ 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
*/