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;
HANDLE ThreadHandle;
PVOID ThreadObject = NULL;
+ TestSymlinks();
+
/* Justify the next comment/statement */
UserModeTest();