1 /*
2 * PROJECT: ReactOS Kernel - Vista+ APIs
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * FILE: lib/drivers/ntoskrnl_vista/fsrtl.c
5 * PURPOSE: FsRtl functions of Vista+
6 * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org>
7 */
8
9 #include <ntdef.h>
10 #include <ntifs.h>
11
12 typedef struct _ECP_LIST
13 {
14 ULONG Signature;
15 ULONG Flags;
16 LIST_ENTRY EcpList;
17 } ECP_LIST, *PECP_LIST;
18
19 typedef ULONG ECP_HEADER_FLAGS;
20
21 typedef struct _ECP_HEADER
22 {
23 ULONG Signature;
24 ULONG Spare;
25 LIST_ENTRY ListEntry;
26 GUID EcpType;
27 PFSRTL_EXTRA_CREATE_PARAMETER_CLEANUP_CALLBACK CleanupCallback;
28 ECP_HEADER_FLAGS Flags;
29 ULONG Size;
30 PVOID ListAllocatedFrom;
31 PVOID Filter;
32 } ECP_HEADER, *PECP_HEADER;
33
34 #define ECP_HEADER_SIZE (sizeof(ECP_HEADER))
35
36 #define ECP_HEADER_TO_CONTEXT(H) ((PVOID)((ULONG_PTR)H + ECP_HEADER_SIZE))
37 #define ECP_CONTEXT_TO_HEADER(C) ((PECP_HEADER)((ULONG_PTR)C - ECP_HEADER_SIZE))
38
39 NTKERNELAPI
40 NTSTATUS
41 NTAPI
FsRtlRemoveDotsFromPath(IN PWSTR OriginalString,IN USHORT PathLength,OUT USHORT * NewLength)42 FsRtlRemoveDotsFromPath(IN PWSTR OriginalString,
43 IN USHORT PathLength,
44 OUT USHORT *NewLength)
45 {
46 USHORT Length, ReadPos, WritePos;
47
48 Length = PathLength / sizeof(WCHAR);
49
50 if (Length == 3 && OriginalString[0] == '\\' && OriginalString[1] == '.' && OriginalString[2] == '.')
51 {
52 return STATUS_IO_REPARSE_DATA_INVALID;
53 }
54
55 if (Length == 2 && OriginalString[0] == '.' && OriginalString[1] == '.')
56 {
57 return STATUS_IO_REPARSE_DATA_INVALID;
58 }
59
60 if (Length > 2 && OriginalString[0] == '.' && OriginalString[1] == '.' && OriginalString[2] == '\\')
61 {
62 return STATUS_IO_REPARSE_DATA_INVALID;
63 }
64
65 for (ReadPos = 0, WritePos = 0; ReadPos < Length; ++WritePos)
66 {
67 for (; ReadPos > 0 && ReadPos < Length; ++ReadPos)
68 {
69 if (ReadPos < Length - 1 && OriginalString[ReadPos] == '\\' && OriginalString[ReadPos + 1] == '\\')
70 {
71 continue;
72 }
73
74 if (OriginalString[ReadPos] != '.')
75 {
76 break;
77 }
78
79 if (ReadPos == Length - 1)
80 {
81 if (OriginalString[ReadPos - 1] == '\\')
82 {
83 if (WritePos > 1)
84 {
85 --WritePos;
86 }
87
88 continue;
89 }
90
91 OriginalString[WritePos] = '.';
92 ++WritePos;
93 continue;
94 }
95
96 if (OriginalString[ReadPos + 1] == '\\')
97 {
98 if (OriginalString[ReadPos - 1] != '\\')
99 {
100 OriginalString[WritePos] = '.';
101 ++WritePos;
102 continue;
103 }
104 }
105 else
106 {
107 if (OriginalString[ReadPos + 1] != '.' || OriginalString[ReadPos - 1] != '\\' ||
108 ((ReadPos != Length - 2) && OriginalString[ReadPos + 2] != '\\'))
109 {
110 OriginalString[WritePos] = '.';
111 ++WritePos;
112 continue;
113 }
114
115 for (WritePos -= 2; (SHORT)WritePos > 0 && OriginalString[WritePos] != '\\'; --WritePos);
116
117 if ((SHORT)WritePos < 0 || OriginalString[WritePos] != '\\')
118 {
119 return STATUS_IO_REPARSE_DATA_INVALID;
120 }
121
122 if (WritePos == 0 && ReadPos == Length - 2)
123 {
124 WritePos = 1;
125 }
126 }
127
128 ++ReadPos;
129 }
130
131 if (ReadPos >= Length)
132 {
133 break;
134 }
135
136 OriginalString[WritePos] = OriginalString[ReadPos];
137 ++ReadPos;
138 }
139
140 *NewLength = WritePos * sizeof(WCHAR);
141
142 while (WritePos < Length)
143 {
144 OriginalString[WritePos++] = UNICODE_NULL;
145 }
146
147 return STATUS_SUCCESS;
148 }
149
150 FORCEINLINE
151 BOOLEAN
IsNullGuid(IN PGUID Guid)152 IsNullGuid(IN PGUID Guid)
153 {
154 if (Guid->Data1 == 0 && Guid->Data2 == 0 && Guid->Data3 == 0 &&
155 ((ULONG *)Guid->Data4)[0] == 0 && ((ULONG *)Guid->Data4)[1] == 0)
156 {
157 return TRUE;
158 }
159
160 return FALSE;
161 }
162
163 FORCEINLINE
164 BOOLEAN
IsEven(IN USHORT Digit)165 IsEven(IN USHORT Digit)
166 {
167 return ((Digit & 1) != 1);
168 }
169
170 NTKERNELAPI
171 NTSTATUS
172 NTAPI
FsRtlValidateReparsePointBuffer(IN ULONG BufferLength,IN PREPARSE_DATA_BUFFER ReparseBuffer)173 FsRtlValidateReparsePointBuffer(IN ULONG BufferLength,
174 IN PREPARSE_DATA_BUFFER ReparseBuffer)
175 {
176 USHORT DataLength;
177 ULONG ReparseTag;
178 PREPARSE_GUID_DATA_BUFFER GuidBuffer;
179
180 /* Validate data size range */
181 if (BufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE || BufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
182 {
183 return STATUS_IO_REPARSE_DATA_INVALID;
184 }
185
186 GuidBuffer = (PREPARSE_GUID_DATA_BUFFER)ReparseBuffer;
187 DataLength = ReparseBuffer->ReparseDataLength;
188 ReparseTag = ReparseBuffer->ReparseTag;
189
190 /* Validate size consistency */
191 if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE != BufferLength && DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE != BufferLength)
192 {
193 return STATUS_IO_REPARSE_DATA_INVALID;
194 }
195
196 /* REPARSE_DATA_BUFFER is reserved for MS tags */
197 if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE == BufferLength && !IsReparseTagMicrosoft(ReparseTag))
198 {
199 return STATUS_IO_REPARSE_DATA_INVALID;
200 }
201
202 /* If that a GUID data buffer, its GUID cannot be null, and it cannot contain a MS tag */
203 if (DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == BufferLength && ((!IsReparseTagMicrosoft(ReparseTag)
204 && IsNullGuid(&GuidBuffer->ReparseGuid)) || (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT || ReparseTag == IO_REPARSE_TAG_SYMLINK)))
205 {
206 return STATUS_IO_REPARSE_DATA_INVALID;
207 }
208
209 /* Check the data for MS non reserved tags */
210 if (!(ReparseTag & 0xFFF0000) && ReparseTag != IO_REPARSE_TAG_RESERVED_ZERO && ReparseTag != IO_REPARSE_TAG_RESERVED_ONE)
211 {
212 /* If that's a mount point, validate the MountPointReparseBuffer branch */
213 if (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
214 {
215 /* We need information */
216 if (DataLength >= REPARSE_DATA_BUFFER_HEADER_SIZE)
217 {
218 /* Substitue must be the first in row */
219 if (!ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset)
220 {
221 /* Substitude must be null-terminated */
222 if (ReparseBuffer->MountPointReparseBuffer.PrintNameOffset == ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL))
223 {
224 /* There must just be the Offset/Length fields + buffer + 2 null chars */
225 if (DataLength == ReparseBuffer->MountPointReparseBuffer.PrintNameLength + ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.SubstituteNameOffset)) + 2 * sizeof(UNICODE_NULL))
226 {
227 return STATUS_SUCCESS;
228 }
229 }
230 }
231 }
232 }
233 else
234 {
235 #define FIELDS_SIZE (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))
236
237 /* If that's not a symlink, accept the MS tag as it */
238 if (ReparseTag != IO_REPARSE_TAG_SYMLINK)
239 {
240 return STATUS_SUCCESS;
241 }
242
243 /* We need information */
244 if (DataLength >= FIELDS_SIZE)
245 {
246 /* Validate lengths */
247 if (ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength && ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength)
248 {
249 /* Validate unicode strings */
250 if (IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) &&
251 IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset))
252 {
253 if ((DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset + ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE)
254 && (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength + ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE))
255 {
256 return STATUS_SUCCESS;
257 }
258 }
259 }
260 }
261 #undef FIELDS_SIZE
262 }
263
264 return STATUS_IO_REPARSE_DATA_INVALID;
265 }
266
267 return STATUS_IO_REPARSE_TAG_INVALID;
268 }
269
270 NTKERNELAPI
271 NTSTATUS
272 NTAPI
FsRtlGetEcpListFromIrp(IN PIRP Irp,OUT PECP_LIST * EcpList)273 FsRtlGetEcpListFromIrp(IN PIRP Irp,
274 OUT PECP_LIST *EcpList)
275 {
276 /* Call Io */
277 return IoGetIrpExtraCreateParameter(Irp, EcpList);
278 }
279
280 NTKERNELAPI
281 NTSTATUS
282 NTAPI
FsRtlGetNextExtraCreateParameter(IN PECP_LIST EcpList,IN PVOID CurrentEcpContext,OUT LPGUID NextEcpType OPTIONAL,OUT PVOID * NextEcpContext,OUT PULONG NextEcpContextSize OPTIONAL)283 FsRtlGetNextExtraCreateParameter(IN PECP_LIST EcpList,
284 IN PVOID CurrentEcpContext,
285 OUT LPGUID NextEcpType OPTIONAL,
286 OUT PVOID *NextEcpContext,
287 OUT PULONG NextEcpContextSize OPTIONAL)
288 {
289 PECP_HEADER CurrentEntry;
290
291 /* If we have no context ... */
292 if (CurrentEcpContext == NULL)
293 {
294 if (IsListEmpty(&EcpList->EcpList))
295 {
296 goto FailEmpty;
297 }
298
299 /* Simply consider first entry */
300 CurrentEntry = CONTAINING_RECORD(EcpList->EcpList.Flink, ECP_HEADER, ListEntry);
301 }
302 else
303 {
304 /* Otherwise, consider the entry matching the given context */
305 CurrentEntry = ECP_CONTEXT_TO_HEADER(CurrentEcpContext);
306
307 /* Make sure we didn't reach the end */
308 if (&CurrentEntry->ListEntry == &EcpList->EcpList)
309 {
310 goto FailEmpty;
311 }
312 }
313
314 /* We must have an entry */
315 if (CurrentEntry == NULL)
316 {
317 goto FailEmpty;
318 }
319
320 /* If caller wants a context, give it */
321 if (NextEcpContext != NULL)
322 {
323 *NextEcpContext = ECP_HEADER_TO_CONTEXT(CurrentEntry);
324 }
325
326 /* Same for its size (which the size minus the header overhead) */
327 if (NextEcpContextSize != NULL)
328 {
329 *NextEcpContextSize = CurrentEntry->Size - sizeof(ECP_HEADER);
330 }
331
332 /* And copy the type if asked to */
333 if (NextEcpType != NULL)
334 {
335 RtlCopyMemory(NextEcpType, &CurrentEntry->EcpType, sizeof(GUID));
336 }
337
338 /* Job done */
339 return STATUS_SUCCESS;
340
341 /* Failure case: just zero everything */
342 FailEmpty:
343 if (NextEcpContext != NULL)
344 {
345 *NextEcpContext = NULL;
346 }
347
348 if (NextEcpContextSize != NULL)
349 {
350 *NextEcpContextSize = 0;
351 }
352
353 if (NextEcpType != NULL)
354 {
355 RtlZeroMemory(NextEcpType, sizeof(GUID));
356 }
357
358 /* And return failure */
359 return STATUS_NOT_FOUND;
360 }
361
362