[NTOSKRNL_VISTA]
[reactos.git] / reactos / sdk / lib / drivers / ntoskrnl_vista / fsrtl.c
1 /*
2 * PROJECT: ReactOS Kernel - Vista+ APIs
3 * LICENSE: GPL v2 - See COPYING in the top level directory
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 NTKERNELAPI
13 NTSTATUS
14 NTAPI
15 FsRtlRemoveDotsFromPath(IN PWSTR OriginalString,
16 IN USHORT PathLength,
17 OUT USHORT *NewLength)
18 {
19 USHORT Length, ReadPos, WritePos;
20
21 Length = PathLength / sizeof(WCHAR);
22
23 if (Length == 3 && OriginalString[0] == '\\' && OriginalString[1] == '.' && OriginalString[2] == '.')
24 {
25 return STATUS_IO_REPARSE_DATA_INVALID;
26 }
27
28 if (Length == 2 && OriginalString[0] == '.' && OriginalString[1] == '.')
29 {
30 return STATUS_IO_REPARSE_DATA_INVALID;
31 }
32
33 if (Length > 2 && OriginalString[0] == '.' && OriginalString[1] == '.' && OriginalString[2] == '\\')
34 {
35 return STATUS_IO_REPARSE_DATA_INVALID;
36 }
37
38 for (ReadPos = 0, WritePos = 0; ReadPos < Length; ++WritePos)
39 {
40 for (; ReadPos > 0 && ReadPos < Length; ++ReadPos)
41 {
42 if (ReadPos < Length - 1 && OriginalString[ReadPos] == '\\' && OriginalString[ReadPos + 1] == '\\')
43 {
44 continue;
45 }
46
47 if (OriginalString[ReadPos] != '.')
48 {
49 break;
50 }
51
52 if (ReadPos == Length - 1)
53 {
54 if (OriginalString[ReadPos - 1] == '\\')
55 {
56 if (WritePos > 1)
57 {
58 --WritePos;
59 }
60
61 continue;
62 }
63
64 OriginalString[WritePos] = '.';
65 ++WritePos;
66 continue;
67 }
68
69 if (OriginalString[ReadPos + 1] == '\\')
70 {
71 if (OriginalString[ReadPos - 1] != '\\')
72 {
73 OriginalString[WritePos] = '.';
74 ++WritePos;
75 continue;
76 }
77 }
78 else
79 {
80 if (OriginalString[ReadPos + 1] != '.' || OriginalString[ReadPos - 1] != '\\' ||
81 ((ReadPos != Length - 2) && OriginalString[ReadPos + 2] != '\\'))
82 {
83 OriginalString[WritePos] = '.';
84 ++WritePos;
85 continue;
86 }
87
88 for (WritePos -= 2; (SHORT)WritePos > 0 && OriginalString[WritePos] != '\\'; --WritePos);
89
90 if ((SHORT)WritePos < 0 || OriginalString[WritePos] != '\\')
91 {
92 return STATUS_IO_REPARSE_DATA_INVALID;
93 }
94
95 if (WritePos == 0 && ReadPos == Length - 2)
96 {
97 WritePos = 1;
98 }
99 }
100
101 ++ReadPos;
102 }
103
104 if (ReadPos >= Length)
105 {
106 break;
107 }
108
109 OriginalString[WritePos] = OriginalString[ReadPos];
110 ++ReadPos;
111 }
112
113 *NewLength = WritePos * sizeof(WCHAR);
114
115 while (WritePos < Length)
116 {
117 OriginalString[WritePos++] = UNICODE_NULL;
118 }
119
120 return STATUS_SUCCESS;
121 }
122
123 FORCEINLINE
124 BOOLEAN
125 IsNullGuid(IN PGUID Guid)
126 {
127 if (Guid->Data1 == 0 && Guid->Data2 == 0 && Guid->Data3 == 0 &&
128 ((ULONG *)Guid->Data4)[0] == 0 && ((ULONG *)Guid->Data4)[1] == 0)
129 {
130 return TRUE;
131 }
132
133 return FALSE;
134 }
135
136 FORCEINLINE
137 BOOLEAN
138 IsEven(IN USHORT Digit)
139 {
140 return ((Digit & 1) != 1);
141 }
142
143 NTKERNELAPI
144 NTSTATUS
145 NTAPI
146 FsRtlValidateReparsePointBuffer(IN ULONG BufferLength,
147 IN PREPARSE_DATA_BUFFER ReparseBuffer)
148 {
149 USHORT DataLength;
150 ULONG ReparseTag;
151 PREPARSE_GUID_DATA_BUFFER GuidBuffer;
152
153 /* Validate data size range */
154 if (BufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE || BufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
155 {
156 return STATUS_IO_REPARSE_DATA_INVALID;
157 }
158
159 GuidBuffer = (PREPARSE_GUID_DATA_BUFFER)ReparseBuffer;
160 DataLength = ReparseBuffer->ReparseDataLength;
161 ReparseTag = ReparseBuffer->ReparseTag;
162
163 /* Validate size consistency */
164 if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE != BufferLength && DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE != BufferLength)
165 {
166 return STATUS_IO_REPARSE_DATA_INVALID;
167 }
168
169 /* REPARSE_DATA_BUFFER is reserved for MS tags */
170 if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE == BufferLength && !IsReparseTagMicrosoft(ReparseTag))
171 {
172 return STATUS_IO_REPARSE_DATA_INVALID;
173 }
174
175 /* If that a GUID data buffer, its GUID cannot be null, and it cannot contain a MS tag */
176 if (DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == BufferLength && ((!IsReparseTagMicrosoft(ReparseTag)
177 && IsNullGuid(&GuidBuffer->ReparseGuid)) || (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT || ReparseTag == IO_REPARSE_TAG_SYMLINK)))
178 {
179 return STATUS_IO_REPARSE_DATA_INVALID;
180 }
181
182 /* Check the data for MS non reserved tags */
183 if (!(ReparseTag & 0xFFF0000) && ReparseTag != IO_REPARSE_TAG_RESERVED_ZERO && ReparseTag != IO_REPARSE_TAG_RESERVED_ONE)
184 {
185 /* If that's a mount point, validate the MountPointReparseBuffer branch */
186 if (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
187 {
188 /* We need information */
189 if (DataLength >= REPARSE_DATA_BUFFER_HEADER_SIZE)
190 {
191 /* Substitue must be the first in row */
192 if (!ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset)
193 {
194 /* Substitude must be null-terminated */
195 if (ReparseBuffer->MountPointReparseBuffer.PrintNameOffset == ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL))
196 {
197 /* There must just be the Offset/Length fields + buffer + 2 null chars */
198 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))
199 {
200 return STATUS_SUCCESS;
201 }
202 }
203 }
204 }
205 }
206 else
207 {
208 #define FIELDS_SIZE (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))
209
210 /* If that's not a symlink, accept the MS tag as it */
211 if (ReparseTag != IO_REPARSE_TAG_SYMLINK)
212 {
213 return STATUS_SUCCESS;
214 }
215
216 /* We need information */
217 if (DataLength >= FIELDS_SIZE)
218 {
219 /* Validate lengths */
220 if (ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength && ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength)
221 {
222 /* Validate unicode strings */
223 if (IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) &&
224 IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset))
225 {
226 if ((DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset + ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE)
227 && (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength + ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE))
228 {
229 return STATUS_SUCCESS;
230 }
231 }
232 }
233 }
234 #undef FIELDS_SIZE
235 }
236
237 return STATUS_IO_REPARSE_DATA_INVALID;
238 }
239
240 return STATUS_IO_REPARSE_TAG_INVALID;
241 }
242