[KMTESTS:IO]
[reactos.git] / rostests / kmtests / ntos_io / IoFilesystem.c
index 9537c6c..bcfc521 100644 (file)
@@ -9,6 +9,275 @@
 
 /* FIXME: Test this stuff on non-FAT volumes */
 
+static
+VOID
+Substitute(
+    _Out_writes_bytes_(BufferSize) PWCHAR Buffer,
+    _In_ ULONG BufferSize,
+    _In_ PCWSTR Template,
+    _In_ PCWSTR SystemDriveName,
+    _In_ PCWSTR SystemRootName)
+{
+    UNICODE_STRING SystemDriveTemplate = RTL_CONSTANT_STRING(L"C:");
+    UNICODE_STRING SystemRootTemplate = RTL_CONSTANT_STRING(L"ReactOS");
+    ULONG SystemDriveLength;
+    ULONG SystemRootLength;
+    PWCHAR Dest = Buffer;
+    UNICODE_STRING String;
+
+    SystemDriveLength = wcslen(SystemDriveName) * sizeof(WCHAR);
+    SystemRootLength = wcslen(SystemRootName) * sizeof(WCHAR);
+
+    RtlInitUnicodeString(&String, Template);
+    ASSERT(String.Length % sizeof(WCHAR) == 0);
+    while (String.Length)
+    {
+        if (RtlPrefixUnicodeString(&SystemDriveTemplate, &String, TRUE))
+        {
+            ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemDriveLength < BufferSize);
+            RtlCopyMemory(Dest,
+                          SystemDriveName,
+                          SystemDriveLength);
+            Dest += SystemDriveLength / sizeof(WCHAR);
+
+            String.Buffer += SystemDriveTemplate.Length / sizeof(WCHAR);
+            String.Length -= SystemDriveTemplate.Length;
+            String.MaximumLength -= SystemDriveTemplate.Length;
+            continue;
+        }
+
+        if (RtlPrefixUnicodeString(&SystemRootTemplate, &String, TRUE))
+        {
+            ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemRootLength < BufferSize);
+            RtlCopyMemory(Dest,
+                          SystemRootName,
+                          SystemRootLength);
+            Dest += SystemRootLength / sizeof(WCHAR);
+
+            String.Buffer += SystemRootTemplate.Length / sizeof(WCHAR);
+            String.Length -= SystemRootTemplate.Length;
+            String.MaximumLength -= SystemRootTemplate.Length;
+            continue;
+        }
+
+        ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
+        *Dest++ = String.Buffer[0];
+
+        String.Buffer++;
+        String.Length -= sizeof(WCHAR);
+        String.MaximumLength -= sizeof(WCHAR);
+    }
+    ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
+    *Dest = UNICODE_NULL;
+}
+
+static
+VOID
+TestRelativeNames(VOID)
+{
+    NTSTATUS Status;
+    struct
+    {
+        PCWSTR ParentPathTemplate;
+        PCWSTR RelativePathTemplate;
+        BOOLEAN IsDirectory;
+        NTSTATUS Status;
+    } Tests[] =
+    {
+        { NULL,                         L"C:\\",                            TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\\\",                          TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\\\\\",                        TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { NULL,                         L"C:\\ReactOS",                     TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\ReactOS\\",                   TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\ReactOS\\\\",                 TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\ReactOS\\\\\\",               TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { NULL,                         L"C:\\\\ReactOS",                   TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\\\ReactOS\\",                 TRUE,   STATUS_SUCCESS },
+        { NULL,                         L"C:\\ReactOS\\explorer.exe",       FALSE,  STATUS_SUCCESS },
+        { NULL,                         L"C:\\ReactOS\\\\explorer.exe",     FALSE,  STATUS_OBJECT_NAME_INVALID },
+        { NULL,                         L"C:\\ReactOS\\explorer.exe\\",     FALSE,  STATUS_OBJECT_NAME_INVALID },
+        { NULL,                         L"C:\\ReactOS\\explorer.exe\\\\",   FALSE,  STATUS_OBJECT_NAME_INVALID },
+        /* This will never return STATUS_NOT_A_DIRECTORY. IsDirectory=TRUE is a little hacky but achieves that without special handling */
+        { NULL,                         L"C:\\ReactOS\\explorer.exe\\\\\\", TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { L"C:\\",                      L"",                                TRUE,   STATUS_SUCCESS },
+        { L"C:\\",                      L"\\",                              TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { L"C:\\",                      L"ReactOS",                         TRUE,   STATUS_SUCCESS },
+        { L"C:\\",                      L"\\ReactOS",                       TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { L"C:\\",                      L"ReactOS\\",                       TRUE,   STATUS_SUCCESS },
+        { L"C:\\",                      L"\\ReactOS\\",                     TRUE,   STATUS_OBJECT_NAME_INVALID },
+        { L"C:\\ReactOS",               L"",                                TRUE,   STATUS_SUCCESS },
+        { L"C:\\ReactOS",               L"explorer.exe",                    FALSE,  STATUS_SUCCESS },
+        { L"C:\\ReactOS\\explorer.exe", L"",                                FALSE,  STATUS_SUCCESS },
+        /* Let's try some nonexistent things */
+        { NULL,                         L"C:\\ReactOS\\IDoNotExist",        FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\IDoNotExist\\file",  FALSE,  STATUS_OBJECT_PATH_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\IDoNotExist\\file?", FALSE,  STATUS_OBJECT_PATH_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\IDoNotExist\\file\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\IDoNotExist\\file\\\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\AmIInvalid?",        FALSE,  STATUS_OBJECT_NAME_INVALID },
+        { NULL,                         L"C:\\ReactOS\\.",                  FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\..",                 FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { NULL,                         L"C:\\ReactOS\\...",                FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\",                      L".",                               FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\",                      L"..",                              FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\",                      L"...",                             FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\ReactOS",               L".",                               FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\ReactOS",               L"..",                              FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+        { L"C:\\ReactOS",               L"...",                             FALSE,  STATUS_OBJECT_NAME_NOT_FOUND },
+    };
+    ULONG i;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatus;
+    UNICODE_STRING ParentPath;
+    UNICODE_STRING RelativePath;
+    HANDLE ParentHandle;
+    HANDLE FileHandle;
+    UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
+    HANDLE SymbolicLinkHandle = NULL;
+    WCHAR LinkNameBuffer[128];
+    UNICODE_STRING SymbolicLinkName;
+    PWSTR SystemDriveName;
+    PWSTR SystemRootName;
+    PWCHAR Buffer = NULL;
+    BOOLEAN TrailingBackslash;
+
+    /* Query \SystemRoot */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &SystemRoot,
+                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = ZwOpenSymbolicLinkObject(&SymbolicLinkHandle,
+                                      GENERIC_READ,
+                                      &ObjectAttributes);
+    if (skip(NT_SUCCESS(Status), "Failed to open SystemRoot, %lx\n", Status))
+        return;
+
+    RtlInitEmptyUnicodeString(&SymbolicLinkName,
+                              LinkNameBuffer,
+                              sizeof(LinkNameBuffer));
+    Status = ZwQuerySymbolicLinkObject(SymbolicLinkHandle,
+                                       &SymbolicLinkName,
+                                       NULL);
+    ObCloseHandle(SymbolicLinkHandle, KernelMode);
+    if (skip(NT_SUCCESS(Status), "Failed to query SystemRoot, %lx\n", Status))
+        return;
+
+    /* Split SymbolicLinkName into drive and path */
+    SystemDriveName = SymbolicLinkName.Buffer;
+    SystemRootName = SymbolicLinkName.Buffer + SymbolicLinkName.Length / sizeof(WCHAR);
+    *SystemRootName-- = UNICODE_NULL;
+    while (*SystemRootName != L'\\')
+    {
+        ASSERT(SystemRootName > SymbolicLinkName.Buffer);
+        SystemRootName--;
+    }
+    *SystemRootName++ = UNICODE_NULL;
+    trace("System Drive: '%ls'\n", SystemDriveName);
+    trace("System Root: '%ls'\n", SystemRootName);
+
+    /* Allocate path buffer */
+    Buffer = ExAllocatePoolWithTag(PagedPool, MAXUSHORT, 'sFmK');
+    if (skip(Buffer != NULL, "No buffer\n"))
+        return;
+
+    /* Finally run some tests! */
+    for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
+    {
+        /* Open parent directory first */
+        ParentHandle = NULL;
+        if (Tests[i].ParentPathTemplate)
+        {
+            Substitute(Buffer,
+                       MAXUSHORT,
+                       Tests[i].ParentPathTemplate,
+                       SystemDriveName,
+                       SystemRootName);
+            RtlInitUnicodeString(&ParentPath, Buffer);
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       &ParentPath,
+                                       OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                                       NULL,
+                                       NULL);
+            Status = ZwOpenFile(&ParentHandle,
+                                GENERIC_READ,
+                                &ObjectAttributes,
+                                &IoStatus,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                0);
+            ok(Status == STATUS_SUCCESS,
+               "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
+            if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
+                continue;
+        }
+
+        /* Now open the relative file: */
+        Substitute(Buffer,
+                   MAXUSHORT,
+                   Tests[i].RelativePathTemplate,
+                   SystemDriveName,
+                   SystemRootName);
+        RtlInitUnicodeString(&RelativePath, Buffer);
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   &RelativePath,
+                                   OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                                   ParentHandle,
+                                   NULL);
+        TrailingBackslash = FALSE;
+        if (wcslen(Buffer) && Buffer[wcslen(Buffer) - 1] == L'\\')
+            TrailingBackslash = TRUE;
+
+        /* (1) No flags */
+        Status = ZwOpenFile(&FileHandle,
+                            GENERIC_READ,
+                            &ObjectAttributes,
+                            &IoStatus,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                            0);
+        ok(Status == Tests[i].Status,
+           "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
+        if (NT_SUCCESS(Status))
+            ObCloseHandle(FileHandle, KernelMode);
+
+        /* (2) Directory File */
+        Status = ZwOpenFile(&FileHandle,
+                            GENERIC_READ,
+                            &ObjectAttributes,
+                            &IoStatus,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                            FILE_DIRECTORY_FILE);
+        if (Tests[i].IsDirectory || (!TrailingBackslash && !NT_SUCCESS(Tests[i].Status)))
+            ok(Status == Tests[i].Status,
+               "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
+        else
+            ok(Status == STATUS_NOT_A_DIRECTORY,
+               "[%lu] Status = %lx, expected STATUS_NOT_A_DIRECTORY\n", i, Status);
+        if (NT_SUCCESS(Status))
+            ObCloseHandle(FileHandle, KernelMode);
+
+        /* (3) Non-Directory File */
+        Status = ZwOpenFile(&FileHandle,
+                            GENERIC_READ,
+                            &ObjectAttributes,
+                            &IoStatus,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                            FILE_NON_DIRECTORY_FILE);
+        if (Tests[i].IsDirectory && NT_SUCCESS(Tests[i].Status))
+            ok(Status == STATUS_FILE_IS_A_DIRECTORY,
+               "[%lu] Status = %lx, expected STATUS_FILE_IS_A_DIRECTORY\n", i, Status);
+        else
+            ok(Status == Tests[i].Status,
+               "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
+        if (NT_SUCCESS(Status))
+            ObCloseHandle(FileHandle, KernelMode);
+
+        /* And close */
+        ObCloseHandle(ParentHandle, KernelMode);
+    }
+
+    ExFreePoolWithTag(Buffer, 'sFmK');
+}
+
 static
 VOID
 TestSharedCacheMap(VOID)
@@ -174,5 +443,6 @@ Cleanup:
 
 START_TEST(IoFilesystem)
 {
+    TestRelativeNames();
     TestSharedCacheMap();
 }