diff --git a/rostests/kmtests/ntos_io/IoCreateFile.c b/rostests/kmtests/ntos_io/IoCreateFile.c index 6de3c42e195..9c35a08d905 100644 --- a/rostests/kmtests/ntos_io/IoCreateFile.c +++ b/rostests/kmtests/ntos_io/IoCreateFile.c @@ -420,6 +420,370 @@ KernelModeTest(IN PVOID Context) static VOID NTAPI +TestSymlinks(VOID) +{ + HANDLE ReparseHandle; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PREPARSE_DATA_BUFFER Reparse; + FILE_DISPOSITION_INFORMATION ToDelete; + PFILE_OBJECT FileObject; + UNICODE_STRING SysDir, Foobar, Regedit; + ULONG Size; + + /* Get Windows/ReactOS directory */ + InitializeObjectAttributes(&ObjectAttributes, + &SystemRoot, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = ZwOpenFile(&ReparseHandle, + FILE_READ_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE); + if (skip(NT_SUCCESS(Status), "Opening \\SystemRoot failed: %lx\n", Status)) + { + return; + } + + Status = ObReferenceObjectByHandle(ReparseHandle, + FILE_READ_DATA, + *IoFileObjectType, + UserMode, + (PVOID *)&FileObject, + NULL); + if (skip(NT_SUCCESS(Status), "Querying name failed: %lx\n", Status)) + { + ZwClose(ReparseHandle); + return; + } + + SysDir.Buffer = ExAllocatePool(NonPagedPool, FileObject->FileName.Length + sizeof(L"\\??\\C:")); + if (skip(SysDir.Buffer != NULL, "Allocating memory failed\n")) + { + ObDereferenceObject(FileObject); + ZwClose(ReparseHandle); + return; + } + + SysDir.Length = sizeof(L"\\??\\C:") - sizeof(UNICODE_NULL); + SysDir.MaximumLength = FileObject->FileName.Length + sizeof(L"\\??\\C:"); + RtlCopyMemory(SysDir.Buffer, L"\\??\\C:", sizeof(L"\\??\\C:") - sizeof(UNICODE_NULL)); + RtlAppendUnicodeStringToString(&SysDir, &FileObject->FileName); + + Foobar.Buffer = ExAllocatePool(NonPagedPool, FileObject->FileName.Length + sizeof(L"\\foobar.exe")); + if (skip(Foobar.Buffer != NULL, "Allocating memory failed\n")) + { + ExFreePool(SysDir.Buffer); + ObDereferenceObject(FileObject); + ZwClose(ReparseHandle); + return; + } + + Foobar.Length = 0; + Foobar.MaximumLength = FileObject->FileName.Length + sizeof(L"\\foobar.exe"); + RtlCopyUnicodeString(&Foobar, &FileObject->FileName); + RtlCopyMemory(&Foobar.Buffer[Foobar.Length / sizeof(WCHAR)], L"\\foobar.exe", sizeof(L"\\foobar.exe") - sizeof(UNICODE_NULL)); + Foobar.Length += (sizeof(L"\\foobar.exe") - sizeof(UNICODE_NULL)); + + Regedit.Buffer = ExAllocatePool(NonPagedPool, FileObject->FileName.Length + sizeof(L"\\regedit.exe")); + if (skip(Regedit.Buffer != NULL, "Allocating memory failed\n")) + { + ExFreePool(Foobar.Buffer); + ExFreePool(SysDir.Buffer); + ObDereferenceObject(FileObject); + ZwClose(ReparseHandle); + return; + } + + Regedit.Length = 0; + Regedit.MaximumLength = FileObject->FileName.Length + sizeof(L"\\regedit.exe"); + RtlCopyUnicodeString(&Regedit, &FileObject->FileName); + RtlCopyMemory(&Regedit.Buffer[Regedit.Length / sizeof(WCHAR)], L"\\regedit.exe", sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + Regedit.Length += (sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + + ObDereferenceObject(FileObject); + ZwClose(ReparseHandle); + + ToDelete.DeleteFile = TRUE; + Size = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + SysDir.Length * 2 + sizeof(L"\\regedit.exe") * 2 - sizeof(L"\\??\\") - sizeof(UNICODE_NULL); + + InitializeObjectAttributes(&ObjectAttributes, + &SystemRootFoobar, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = ZwCreateFile(&ReparseHandle, + GENERIC_READ | GENERIC_WRITE | DELETE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SUPERSEDE, + FILE_NON_DIRECTORY_FILE, + NULL, + 0); + ok_eq_hex(Status, STATUS_SUCCESS); + if (skip(NT_SUCCESS(Status), "Creating file failed: %lx\n", Status)) + { + ExFreePool(Regedit.Buffer); + ExFreePool(Foobar.Buffer); + ExFreePool(SysDir.Buffer); + return; + } + + Reparse = ExAllocatePool(NonPagedPool, Size); + RtlZeroMemory(Reparse, Size); + Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK; + Reparse->ReparseDataLength = 12 + SysDir.Length * 2 + sizeof(L"\\regedit.exe") * 2 - sizeof(L"\\??\\") - sizeof(UNICODE_NULL); + Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength = SysDir.Length + sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL); + Reparse->SymbolicLinkReparseBuffer.PrintNameLength = SysDir.Length + sizeof(L"\\regedit.exe") - sizeof(L"\\??\\"); + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset = Reparse->SymbolicLinkReparseBuffer.PrintNameLength; + RtlCopyMemory(Reparse->SymbolicLinkReparseBuffer.PathBuffer, + (WCHAR *)((ULONG_PTR)SysDir.Buffer + sizeof(L"\\??\\") - sizeof(UNICODE_NULL)), + SysDir.Length - sizeof(L"\\??\\") + sizeof(UNICODE_NULL)); + RtlCopyMemory((WCHAR *)((ULONG_PTR)Reparse->SymbolicLinkReparseBuffer.PathBuffer + SysDir.Length - sizeof(L"\\??\\") + sizeof(UNICODE_NULL)), + L"\\regedit.exe", sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + RtlCopyMemory((WCHAR *)((ULONG_PTR)Reparse->SymbolicLinkReparseBuffer.PathBuffer + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset), + SysDir.Buffer, SysDir.Length); + RtlCopyMemory((WCHAR *)((ULONG_PTR)Reparse->SymbolicLinkReparseBuffer.PathBuffer + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset + SysDir.Length), + L"\\regedit.exe", sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + + Status = ZwFsControlFile(ReparseHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + FSCTL_SET_REPARSE_POINT, + Reparse, + Size, + NULL, + 0); + ok_eq_hex(Status, STATUS_SUCCESS); + if (!NT_SUCCESS(Status)) + { + ZwClose(ReparseHandle); + + Status = ZwCreateFile(&ReparseHandle, + FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_SUPERSEDE, + FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, + 0); + if (skip(NT_SUCCESS(Status), "Creating symlink failed: %lx\n", Status)) + { + Status = ZwOpenFile(&ReparseHandle, + DELETE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_DELETE, + FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE); + ok_eq_hex(Status, STATUS_SUCCESS); + ZwClose(ReparseHandle); + ExFreePool(Regedit.Buffer); + ExFreePool(Foobar.Buffer); + ExFreePool(SysDir.Buffer); + ExFreePool(Reparse); + return; + } + + Status = ZwFsControlFile(ReparseHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + FSCTL_SET_REPARSE_POINT, + Reparse, + Size, + NULL, + 0); + } + + if (skip(NT_SUCCESS(Status), "Creating symlink failed: %lx\n", Status)) + { + ZwSetInformationFile(ReparseHandle, + &IoStatusBlock, + &ToDelete, + sizeof(ToDelete), + FileDispositionInformation); + ZwClose(ReparseHandle); + ExFreePool(Regedit.Buffer); + ExFreePool(Foobar.Buffer); + ExFreePool(SysDir.Buffer); + ExFreePool(Reparse); + return; + } + + ZwClose(ReparseHandle); + + Status = ZwCreateFile(&ReparseHandle, + GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE, + NULL, + 0); + ok(Status == STATUS_SUCCESS || /* Windows Vista+ */ + Status == STATUS_IO_REPARSE_TAG_NOT_HANDLED, /* Windows 2003 (SP1, SP2) */ + "ZwCreateFile returned unexpected status: %lx\n", Status); + if (NT_SUCCESS(Status)) + { + Status = ObReferenceObjectByHandle(ReparseHandle, + FILE_READ_DATA, + *IoFileObjectType, + UserMode, + (PVOID *)&FileObject, + NULL); + ok_eq_hex(Status, STATUS_SUCCESS); + if (NT_SUCCESS(Status)) + { + ok(RtlCompareUnicodeString(&Regedit, &FileObject->FileName, TRUE) == 0, + "Expected: %wZ. Opened: %wZ\n", &Regedit, &FileObject->FileName); + ObDereferenceObject(FileObject); + } + + ZwClose(ReparseHandle); + } + + ExFreePool(Regedit.Buffer); + + Status = IoCreateFile(&ReparseHandle, + GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE, + NULL, + 0, + CreateFileTypeNone, + NULL, + IO_NO_PARAMETER_CHECKING | IO_STOP_ON_SYMLINK); + ok(Status == STATUS_STOPPED_ON_SYMLINK || /* Windows Vista+ */ + Status == STATUS_IO_REPARSE_TAG_NOT_HANDLED, /* Windows 2003 (SP1, SP2) */ + "ZwCreateFile returned unexpected status: %lx\n", Status); + if (NT_SUCCESS(Status)) + { + ZwClose(ReparseHandle); + } + + Status = ZwCreateFile(&ReparseHandle, + GENERIC_READ | GENERIC_WRITE | DELETE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, + 0); + if (skip(NT_SUCCESS(Status), "Creating opening reparse point: %lx\n", Status)) + { + Status = ZwOpenFile(&ReparseHandle, + DELETE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_DELETE, + FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE); + ok_eq_hex(Status, STATUS_SUCCESS); + ZwClose(ReparseHandle); + ExFreePool(Foobar.Buffer); + ExFreePool(SysDir.Buffer); + ExFreePool(Reparse); + return; + } + + Status = ObReferenceObjectByHandle(ReparseHandle, + FILE_READ_DATA, + *IoFileObjectType, + UserMode, + (PVOID *)&FileObject, + NULL); + ok_eq_hex(Status, STATUS_SUCCESS); + if (NT_SUCCESS(Status)) + { + ok(RtlCompareUnicodeString(&Foobar, &FileObject->FileName, TRUE) == 0, + "Expected: %wZ. Opened: %wZ\n", &Foobar, &FileObject->FileName); + ObDereferenceObject(FileObject); + } + + ExFreePool(Foobar.Buffer); + + RtlZeroMemory(Reparse, Size); + Status = ZwFsControlFile(ReparseHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, + Reparse, + Size); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_hex(IoStatusBlock.Information, Size); + if (NT_SUCCESS(Status)) + { + PWSTR Buffer; + UNICODE_STRING ReparsePath, FullPath; + + ok_eq_hex(Reparse->ReparseTag, IO_REPARSE_TAG_SYMLINK); + ok_eq_hex(Reparse->ReparseDataLength, 12 + SysDir.Length * 2 + sizeof(L"\\regedit.exe") * 2 - sizeof(L"\\??\\") - sizeof(UNICODE_NULL)); + ok_eq_hex(Reparse->SymbolicLinkReparseBuffer.Flags, 0); + + FullPath.Length = 0; + FullPath.MaximumLength = SysDir.Length + sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL); + Buffer = FullPath.Buffer = ExAllocatePool(NonPagedPool, FullPath.MaximumLength); + if (!skip(Buffer != NULL, "Memory allocation failed!\n")) + { + RtlCopyUnicodeString(&FullPath, &SysDir); + RtlCopyMemory(&FullPath.Buffer[FullPath.Length / sizeof(WCHAR)], L"\\regedit.exe", sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + FullPath.Length += (sizeof(L"\\regedit.exe") - sizeof(UNICODE_NULL)); + ReparsePath.Buffer = (PWSTR)((ULONG_PTR)Reparse->SymbolicLinkReparseBuffer.PathBuffer + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset); + ReparsePath.Length = ReparsePath.MaximumLength = Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength; + ok(RtlCompareUnicodeString(&ReparsePath, &FullPath, TRUE) == 0, "Expected: %wZ. Got: %wZ\n", &ReparsePath, &FullPath); + + FullPath.Length -= (sizeof(L"\\??\\") - sizeof(UNICODE_NULL)); + FullPath.MaximumLength -= (sizeof(L"\\??\\") - sizeof(UNICODE_NULL)); + FullPath.Buffer = (PWSTR)((ULONG_PTR)Buffer + sizeof(L"\\??\\") - sizeof(UNICODE_NULL)); + ReparsePath.Buffer = (PWSTR)((ULONG_PTR)Reparse->SymbolicLinkReparseBuffer.PathBuffer + Reparse->SymbolicLinkReparseBuffer.PrintNameOffset); + ReparsePath.Length = ReparsePath.MaximumLength = Reparse->SymbolicLinkReparseBuffer.PrintNameLength; + ok(RtlCompareUnicodeString(&ReparsePath, &FullPath, TRUE) == 0, "Expected: %wZ. Got: %wZ\n", &ReparsePath, &FullPath); + + ExFreePool(Buffer); + } + } + + ExFreePool(SysDir.Buffer); + ExFreePool(Reparse); + + ZwSetInformationFile(ReparseHandle, + &IoStatusBlock, + &ToDelete, + sizeof(ToDelete), + FileDispositionInformation); + ZwClose(ReparseHandle); +} + +//static +VOID +NTAPI UserModeTest(VOID) { NTSTATUS Status; @@ -512,6 +876,8 @@ START_TEST(IoCreateFile) HANDLE ThreadHandle; PVOID ThreadObject = NULL; + TestSymlinks(); + /* Justify the next comment/statement */ UserModeTest();