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
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
54 TestUnload(
55     _In_ PDRIVER_OBJECT DriverObject)
56 {
57     PAGED_CODE();
58 }
59 
60 static volatile long gNoLinks = FALSE;
61 
62 static
63 NTSTATUS
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 = ExAllocatePoolWithTag(NonPagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 'FwrI');
98             Reparse = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
99 
100             RtlZeroMemory(Reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
101             Reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
102             Reparse->ReparseDataLength = 12 + sizeof(L"\\??\\C:\\Documents and Settings");
103             Reparse->MountPointReparseBuffer.SubstituteNameLength = sizeof(L"\\??\\C:\\Documents and Settings") - sizeof(UNICODE_NULL);
104             Reparse->MountPointReparseBuffer.PrintNameOffset = sizeof(L"\\??\\C:\\Documents and Settings");
105             RtlCopyMemory(Reparse->MountPointReparseBuffer.PathBuffer, L"\\??\\C:\\Documents and Settings", sizeof(L"\\??\\C:\\Documents and Settings"));
106             Irp->IoStatus.Information = IO_REPARSE_TAG_MOUNT_POINT;
107             Status = STATUS_REPARSE;
108         }
109         else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
110             IoStack->FileObject->FileName.Buffer[1] == 'S')
111         {
112             if (IoStack->Flags & SL_STOP_ON_SYMLINK)
113             {
114                 Status = STATUS_STOPPED_ON_SYMLINK;
115             }
116             else
117             {
118                 PREPARSE_DATA_BUFFER Reparse;
119 
120                 Irp->Tail.Overlay.AuxiliaryBuffer = ExAllocatePoolWithTag(NonPagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 'FwrI');
121                 Reparse = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
122 
123                 RtlZeroMemory(Reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
124                 Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK;
125                 Reparse->ReparseDataLength = 12 + sizeof(L"\\??\\C:\\Documents and Settings");
126                 Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength = sizeof(L"\\??\\C:\\Documents and Settings") - sizeof(UNICODE_NULL);
127                 Reparse->SymbolicLinkReparseBuffer.PrintNameOffset = sizeof(L"\\??\\C:\\Documents and Settings");
128                 RtlCopyMemory(Reparse->SymbolicLinkReparseBuffer.PathBuffer, L"\\??\\C:\\Documents and Settings", sizeof(L"\\??\\C:\\Documents and Settings"));
129                 Irp->IoStatus.Information = IO_REPARSE_TAG_SYMLINK;
130                 Status = STATUS_REPARSE;
131             }
132         }
133         else
134         {
135             Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Fcb), 'FwrI');
136             RtlZeroMemory(Fcb, sizeof(*Fcb));
137             ExInitializeFastMutex(&Fcb->HeaderMutex);
138             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
139             Fcb->Header.AllocationSize.QuadPart = 0;
140             Fcb->Header.FileSize.QuadPart = 0;
141             Fcb->Header.ValidDataLength.QuadPart = 0;
142             IoStack->FileObject->FsContext = Fcb;
143             IoStack->FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
144 
145             Irp->IoStatus.Information = FILE_OPENED;
146             Status = STATUS_SUCCESS;
147         }
148     }
149     else if (IoStack->MajorFunction == IRP_MJ_CLEANUP)
150     {
151         KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
152         CcUninitializeCacheMap(IoStack->FileObject, NULL, &CacheUninitEvent);
153         KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
154         Fcb = IoStack->FileObject->FsContext;
155         ExFreePoolWithTag(Fcb, 'FwrI');
156         IoStack->FileObject->FsContext = NULL;
157         Status = STATUS_SUCCESS;
158     }
159 
160     Irp->IoStatus.Status = Status;
161     IoCompleteRequest(Irp, IO_NO_INCREMENT);
162 
163     return Status;
164 }
165 
166 static UNICODE_STRING FileObjectFileName = RTL_CONSTANT_STRING(L"\\NonSymlinked");
167 static UNICODE_STRING DocumentsAndSettings = RTL_CONSTANT_STRING(L"\\Documents and Settings");
168 
169 static
170 NTSTATUS
171 TestIoCreateFile(
172     IN PUNICODE_STRING Path)
173 {
174     OBJECT_ATTRIBUTES ObjectAttributes;
175     IO_STATUS_BLOCK IoStatusBlock;
176     HANDLE Handle;
177     NTSTATUS Status;
178     PFILE_OBJECT FileObject;
179 
180     InitializeObjectAttributes(&ObjectAttributes,
181                                Path,
182                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
183                                NULL,
184                                NULL);
185     Status = IoCreateFile(&Handle,
186                           GENERIC_READ,
187                           &ObjectAttributes,
188                           &IoStatusBlock,
189                           NULL,
190                           FILE_ATTRIBUTE_NORMAL,
191                           FILE_SHARE_READ | FILE_SHARE_WRITE,
192                           FILE_OPEN,
193                           FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
194                           NULL,
195                           0,
196                           CreateFileTypeNone,
197                           NULL,
198                           IO_NO_PARAMETER_CHECKING | (gNoLinks ? IO_STOP_ON_SYMLINK : 0));
199     if (NT_SUCCESS(Status))
200     {
201         NTSTATUS IntStatus;
202 
203         IntStatus = ObReferenceObjectByHandle(Handle,
204                                               FILE_READ_DATA,
205                                               *IoFileObjectType,
206                                               KernelMode,
207                                               (PVOID *)&FileObject,
208                                               NULL);
209         ok_eq_hex(IntStatus, STATUS_SUCCESS);
210         if (NT_SUCCESS(IntStatus))
211         {
212             ok(RtlCompareUnicodeString(&FileObjectFileName, &FileObject->FileName, TRUE) == 0 ||
213                RtlCompareUnicodeString(&DocumentsAndSettings, &FileObject->FileName, TRUE) == 0,
214                "Expected: %wZ or %wZ. Opened: %wZ\n", &FileObjectFileName, &DocumentsAndSettings, &FileObject->FileName);
215             ObDereferenceObject(FileObject);
216         }
217 
218         IntStatus = ObCloseHandle(Handle, KernelMode);
219         ok_eq_hex(IntStatus, STATUS_SUCCESS);
220     }
221 
222     return Status;
223 }
224 
225 static
226 NTSTATUS
227 TestMessageHandler(
228     IN PDEVICE_OBJECT DeviceObject,
229     IN ULONG ControlCode,
230     IN PVOID Buffer OPTIONAL,
231     IN SIZE_T InLength,
232     IN OUT PSIZE_T OutLength)
233 {
234     NTSTATUS Status = STATUS_SUCCESS;
235 
236     PAGED_CODE();
237 
238     switch (ControlCode)
239     {
240         case IOCTL_DISABLE_SYMLINK:
241         {
242             if (InterlockedExchange(&gNoLinks, TRUE) == TRUE)
243             {
244                 Status = STATUS_UNSUCCESSFUL;
245             }
246 
247             break;
248         }
249         case IOCTL_CALL_CREATE:
250         {
251             ANSI_STRING Path;
252             UNICODE_STRING PathW;
253 
254             ok(Buffer != NULL, "Buffer is NULL\n");
255             Path.Length = Path.MaximumLength = (USHORT)InLength;
256             Path.Buffer = Buffer;
257 
258             Status = RtlAnsiStringToUnicodeString(&PathW, &Path, TRUE);
259             ok_eq_hex(Status, STATUS_SUCCESS);
260 
261             Status = TestIoCreateFile(&PathW);
262 
263             RtlFreeUnicodeString(&PathW);
264 
265             break;
266         }
267         default:
268             return STATUS_NOT_SUPPORTED;
269     }
270 
271     return Status;
272 }
273