From 549ecc5195051ac840974d7a23a710a09a4b671b Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Fri, 25 Sep 2015 15:57:28 +0000 Subject: [PATCH] [KMTESTS:IO] Add more tests for IoCreateFile(), this time purely dealing with symlinks reparse points. They won't run on ReactOS (obviously) as we don't have RW NTFS support. On Windows, they show interesting results.... With Windows 2003, creating the symlink is allowed and works, but then Windows is totally unable to deal with it and keeps complaining about an unhandled reparse tag. The only way to open it is to open the reparse point itself. Not that working symlinks... Not sure which part is not able to handle the said reparse tag. NTFS? Io? With Windows Vista+, it just works fine. Symlink is created & functionnal. Broken logic! svn path=/trunk/; revision=69353 --- rostests/kmtests/ntos_io/IoCreateFile.c | 366 ++++++++++++++++++++++++ 1 file changed, 366 insertions(+) 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();