1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for reparse point operations
5  * PROGRAMMER:      Pierre Schweitzer <pierre@reactos.org>
6  */
7 
8 #include <kmt_test.h>
9 
10 #define NDEBUG
11 #include <debug.h>
12 
13 #include "IoCreateFile.h"
14 
15 typedef struct _TEST_FCB
16 {
17     FSRTL_ADVANCED_FCB_HEADER Header;
18     SECTION_OBJECT_POINTERS SectionObjectPointers;
19     FAST_MUTEX HeaderMutex;
20 } TEST_FCB, *PTEST_FCB;
21 
22 static KMT_IRP_HANDLER TestIrpHandler;
23 static KMT_MESSAGE_HANDLER TestMessageHandler;
24 
25 static PFILE_OBJECT TestFileObject;
26 static PDEVICE_OBJECT TestDeviceObject;
27 
28 NTSTATUS
TestEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PCUNICODE_STRING RegistryPath,_Out_ PCWSTR * DeviceName,_Inout_ INT * Flags)29 TestEntry(
30     _In_ PDRIVER_OBJECT DriverObject,
31     _In_ PCUNICODE_STRING RegistryPath,
32     _Out_ PCWSTR *DeviceName,
33     _Inout_ INT *Flags)
34 {
35     NTSTATUS Status = STATUS_SUCCESS;
36 
37     PAGED_CODE();
38 
39     UNREFERENCED_PARAMETER(RegistryPath);
40 
41     *DeviceName = L"IoCreateFile";
42     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
43              TESTENTRY_BUFFERED_IO_DEVICE |
44              TESTENTRY_NO_READONLY_DEVICE;
45 
46     KmtRegisterIrpHandler(IRP_MJ_CREATE, NULL, TestIrpHandler);
47     KmtRegisterIrpHandler(IRP_MJ_CLEANUP, NULL, TestIrpHandler);
48     KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
49 
50     return Status;
51 }
52 
53 VOID
TestUnload(_In_ PDRIVER_OBJECT DriverObject)54 TestUnload(
55     _In_ PDRIVER_OBJECT DriverObject)
56 {
57     PAGED_CODE();
58 }
59 
60 static volatile long gNoLinks = FALSE;
61 
62 static
63 NTSTATUS
TestIrpHandler(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_In_ PIO_STACK_LOCATION IoStack)64 TestIrpHandler(
65     _In_ PDEVICE_OBJECT DeviceObject,
66     _In_ PIRP Irp,
67     _In_ PIO_STACK_LOCATION IoStack)
68 {
69     NTSTATUS Status;
70     PTEST_FCB Fcb;
71     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
72 
73     PAGED_CODE();
74 
75     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
76     ASSERT(IoStack->MajorFunction == IRP_MJ_CREATE ||
77            IoStack->MajorFunction == IRP_MJ_CLEANUP);
78 
79     Status = STATUS_NOT_SUPPORTED;
80     Irp->IoStatus.Information = 0;
81 
82     if (IoStack->MajorFunction == IRP_MJ_CREATE)
83     {
84         ok((IoStack->Parameters.Create.Options & FILE_OPEN_REPARSE_POINT) == 0, "FILE_OPEN_REPARSE_POINT set\n");
85         ok((IoStack->Flags == 0 && !gNoLinks) || (IoStack->Flags == SL_STOP_ON_SYMLINK && gNoLinks), "IoStack->Flags = %lx\n", IoStack->Flags);
86 
87         if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR))
88         {
89             TestDeviceObject = DeviceObject;
90             TestFileObject = IoStack->FileObject;
91         }
92         if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
93             IoStack->FileObject->FileName.Buffer[1] == 'M')
94         {
95             PREPARSE_DATA_BUFFER Reparse;
96 
97             Irp->Tail.Overlay.AuxiliaryBuffer = ExAllocatePoolZero(NonPagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 'FwrI');
98             Reparse = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
99 
100             if (!Reparse)
101             {
102                 Status = STATUS_INSUFFICIENT_RESOURCES;
103                 goto Finish;
104             }
105 
106             Reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
107             Reparse->ReparseDataLength = 12 + sizeof(L"\\??\\C:\\Documents and Settings");
108             Reparse->MountPointReparseBuffer.SubstituteNameLength = sizeof(L"\\??\\C:\\Documents and Settings") - sizeof(UNICODE_NULL);
109             Reparse->MountPointReparseBuffer.PrintNameOffset = sizeof(L"\\??\\C:\\Documents and Settings");
110             RtlCopyMemory(Reparse->MountPointReparseBuffer.PathBuffer, L"\\??\\C:\\Documents and Settings", sizeof(L"\\??\\C:\\Documents and Settings"));
111             Irp->IoStatus.Information = IO_REPARSE_TAG_MOUNT_POINT;
112             Status = STATUS_REPARSE;
113         }
114         else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
115             IoStack->FileObject->FileName.Buffer[1] == 'S')
116         {
117             PREPARSE_DATA_BUFFER Reparse;
118 
119             if (IoStack->Flags & SL_STOP_ON_SYMLINK)
120             {
121                 Status = STATUS_STOPPED_ON_SYMLINK;
122                 goto Finish;
123             }
124 
125             Irp->Tail.Overlay.AuxiliaryBuffer = ExAllocatePoolZero(NonPagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 'FwrI');
126             Reparse = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
127 
128             if (!Reparse)
129             {
130                 Status = STATUS_INSUFFICIENT_RESOURCES;
131                 goto Finish;
132             }
133 
134             Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK;
135             Reparse->ReparseDataLength = 12 + sizeof(L"\\??\\C:\\Documents and Settings");
136             Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength = sizeof(L"\\??\\C:\\Documents and Settings") - sizeof(UNICODE_NULL);
137             Reparse->SymbolicLinkReparseBuffer.PrintNameOffset = sizeof(L"\\??\\C:\\Documents and Settings");
138             RtlCopyMemory(Reparse->SymbolicLinkReparseBuffer.PathBuffer, L"\\??\\C:\\Documents and Settings", sizeof(L"\\??\\C:\\Documents and Settings"));
139             Irp->IoStatus.Information = IO_REPARSE_TAG_SYMLINK;
140             Status = STATUS_REPARSE;
141         }
142         else
143         {
144             Fcb = ExAllocatePoolZero(NonPagedPool, sizeof(*Fcb), 'FwrI');
145 
146             if (!Fcb)
147             {
148                 Status = STATUS_INSUFFICIENT_RESOURCES;
149                 goto Finish;
150             }
151 
152             ExInitializeFastMutex(&Fcb->HeaderMutex);
153             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
154             Fcb->Header.AllocationSize.QuadPart = 0;
155             Fcb->Header.FileSize.QuadPart = 0;
156             Fcb->Header.ValidDataLength.QuadPart = 0;
157             IoStack->FileObject->FsContext = Fcb;
158             IoStack->FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
159 
160             Irp->IoStatus.Information = FILE_OPENED;
161             Status = STATUS_SUCCESS;
162         }
163     }
164     else if (IoStack->MajorFunction == IRP_MJ_CLEANUP)
165     {
166         KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
167         CcUninitializeCacheMap(IoStack->FileObject, NULL, &CacheUninitEvent);
168         KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
169         Fcb = IoStack->FileObject->FsContext;
170         ExFreePoolWithTag(Fcb, 'FwrI');
171         IoStack->FileObject->FsContext = NULL;
172         Status = STATUS_SUCCESS;
173     }
174 
175 Finish:
176     Irp->IoStatus.Status = Status;
177     IoCompleteRequest(Irp, IO_NO_INCREMENT);
178 
179     return Status;
180 }
181 
182 static UNICODE_STRING FileObjectFileName = RTL_CONSTANT_STRING(L"\\NonSymlinked");
183 static UNICODE_STRING DocumentsAndSettings = RTL_CONSTANT_STRING(L"\\Documents and Settings");
184 
185 static
186 NTSTATUS
TestIoCreateFile(IN PUNICODE_STRING Path)187 TestIoCreateFile(
188     IN PUNICODE_STRING Path)
189 {
190     OBJECT_ATTRIBUTES ObjectAttributes;
191     IO_STATUS_BLOCK IoStatusBlock;
192     HANDLE Handle;
193     NTSTATUS Status;
194     PFILE_OBJECT FileObject;
195 
196     InitializeObjectAttributes(&ObjectAttributes,
197                                Path,
198                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
199                                NULL,
200                                NULL);
201     Status = IoCreateFile(&Handle,
202                           GENERIC_READ,
203                           &ObjectAttributes,
204                           &IoStatusBlock,
205                           NULL,
206                           FILE_ATTRIBUTE_NORMAL,
207                           FILE_SHARE_READ | FILE_SHARE_WRITE,
208                           FILE_OPEN,
209                           FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
210                           NULL,
211                           0,
212                           CreateFileTypeNone,
213                           NULL,
214                           IO_NO_PARAMETER_CHECKING | (gNoLinks ? IO_STOP_ON_SYMLINK : 0));
215     if (NT_SUCCESS(Status))
216     {
217         NTSTATUS IntStatus;
218 
219         IntStatus = ObReferenceObjectByHandle(Handle,
220                                               FILE_READ_DATA,
221                                               *IoFileObjectType,
222                                               KernelMode,
223                                               (PVOID *)&FileObject,
224                                               NULL);
225         ok_eq_hex(IntStatus, STATUS_SUCCESS);
226         if (NT_SUCCESS(IntStatus))
227         {
228             ok(RtlCompareUnicodeString(&FileObjectFileName, &FileObject->FileName, TRUE) == 0 ||
229                RtlCompareUnicodeString(&DocumentsAndSettings, &FileObject->FileName, TRUE) == 0,
230                "Expected: %wZ or %wZ. Opened: %wZ\n", &FileObjectFileName, &DocumentsAndSettings, &FileObject->FileName);
231             ObDereferenceObject(FileObject);
232         }
233 
234         IntStatus = ObCloseHandle(Handle, KernelMode);
235         ok_eq_hex(IntStatus, STATUS_SUCCESS);
236     }
237 
238     return Status;
239 }
240 
241 static
242 NTSTATUS
TestMessageHandler(IN PDEVICE_OBJECT DeviceObject,IN ULONG ControlCode,IN PVOID Buffer OPTIONAL,IN SIZE_T InLength,IN OUT PSIZE_T OutLength)243 TestMessageHandler(
244     IN PDEVICE_OBJECT DeviceObject,
245     IN ULONG ControlCode,
246     IN PVOID Buffer OPTIONAL,
247     IN SIZE_T InLength,
248     IN OUT PSIZE_T OutLength)
249 {
250     NTSTATUS Status = STATUS_SUCCESS;
251 
252     PAGED_CODE();
253 
254     switch (ControlCode)
255     {
256         case IOCTL_DISABLE_SYMLINK:
257         {
258             if (InterlockedExchange(&gNoLinks, TRUE) == TRUE)
259             {
260                 Status = STATUS_UNSUCCESSFUL;
261             }
262 
263             break;
264         }
265         case IOCTL_CALL_CREATE:
266         {
267             ANSI_STRING Path;
268             UNICODE_STRING PathW;
269 
270             ok(Buffer != NULL, "Buffer is NULL\n");
271             Path.Length = Path.MaximumLength = (USHORT)InLength;
272             Path.Buffer = Buffer;
273 
274             Status = RtlAnsiStringToUnicodeString(&PathW, &Path, TRUE);
275             ok_eq_hex(Status, STATUS_SUCCESS);
276 
277             Status = TestIoCreateFile(&PathW);
278 
279             RtlFreeUnicodeString(&PathW);
280 
281             break;
282         }
283         default:
284             return STATUS_NOT_SUPPORTED;
285     }
286 
287     return Status;
288 }
289