xref: /reactos/sdk/lib/drivers/ntoskrnl_vista/fsrtl.c (revision 5100859e)
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 
243