[KMTESTS:IO]
authorPierre Schweitzer <pierre@reactos.org>
Fri, 25 Sep 2015 15:57:28 +0000 (15:57 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Fri, 25 Sep 2015 15:57:28 +0000 (15:57 +0000)
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

index 6de3c42..9c35a08 100644 (file)
@@ -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();