xref: /reactos/drivers/filesystems/btrfs/fsrtl.c (revision e1338178)
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 <ntifs.h>
10 #include <ntdef.h>
11 
12 FORCEINLINE
13 BOOLEAN
14 IsNullGuid(IN PGUID Guid)
15 {
16     if (Guid->Data1 == 0 && Guid->Data2 == 0 && Guid->Data3 == 0 &&
17         ((ULONG *)Guid->Data4)[0] == 0 && ((ULONG *)Guid->Data4)[1] == 0)
18     {
19         return TRUE;
20     }
21 
22     return FALSE;
23 }
24 
25 FORCEINLINE
26 BOOLEAN
27 IsEven(IN USHORT Digit)
28 {
29     return ((Digit & 1) != 1);
30 }
31 
32 NTSTATUS compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer)
33 {
34     USHORT DataLength;
35     ULONG ReparseTag;
36     PREPARSE_GUID_DATA_BUFFER GuidBuffer;
37 
38     /* Validate data size range */
39     if (BufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE || BufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
40     {
41         return STATUS_IO_REPARSE_DATA_INVALID;
42     }
43 
44     GuidBuffer = (PREPARSE_GUID_DATA_BUFFER)ReparseBuffer;
45     DataLength = ReparseBuffer->ReparseDataLength;
46     ReparseTag = ReparseBuffer->ReparseTag;
47 
48     /* Validate size consistency */
49     if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE != BufferLength && DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE != BufferLength)
50     {
51         return STATUS_IO_REPARSE_DATA_INVALID;
52     }
53 
54     /* REPARSE_DATA_BUFFER is reserved for MS tags */
55     if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE == BufferLength && !IsReparseTagMicrosoft(ReparseTag))
56     {
57         return STATUS_IO_REPARSE_DATA_INVALID;
58     }
59 
60     /* If that a GUID data buffer, its GUID cannot be null, and it cannot contain a MS tag */
61     if (DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == BufferLength && ((!IsReparseTagMicrosoft(ReparseTag)
62         && IsNullGuid(&GuidBuffer->ReparseGuid)) || (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT || ReparseTag == IO_REPARSE_TAG_SYMLINK)))
63     {
64         return STATUS_IO_REPARSE_DATA_INVALID;
65     }
66 
67     /* Check the data for MS non reserved tags */
68     if (!(ReparseTag & 0xFFF0000) && ReparseTag != IO_REPARSE_TAG_RESERVED_ZERO && ReparseTag != IO_REPARSE_TAG_RESERVED_ONE)
69     {
70         /* If that's a mount point, validate the MountPointReparseBuffer branch */
71         if (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
72         {
73             /* We need information */
74             if (DataLength >= REPARSE_DATA_BUFFER_HEADER_SIZE)
75             {
76                 /* Substitue must be the first in row */
77                 if (!ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset)
78                 {
79                     /* Substitude must be null-terminated */
80                     if (ReparseBuffer->MountPointReparseBuffer.PrintNameOffset == ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL))
81                     {
82                         /* There must just be the Offset/Length fields + buffer + 2 null chars */
83                         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))
84                         {
85                             return STATUS_SUCCESS;
86                         }
87                     }
88                 }
89             }
90         }
91         else
92         {
93 #define FIELDS_SIZE (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))
94 
95             /* If that's not a symlink, accept the MS tag as it */
96             if (ReparseTag != IO_REPARSE_TAG_SYMLINK)
97             {
98                 return STATUS_SUCCESS;
99             }
100 
101             /* We need information */
102             if (DataLength >= FIELDS_SIZE)
103             {
104                 /* Validate lengths */
105                 if (ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength && ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength)
106                 {
107                     /* Validate unicode strings */
108                     if (IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) &&
109                         IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset))
110                     {
111                         if ((DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset + ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE)
112                             && (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength + ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE))
113                         {
114                             return STATUS_SUCCESS;
115                         }
116                     }
117                 }
118             }
119 #undef FIELDS_SIZE
120         }
121 
122         return STATUS_IO_REPARSE_DATA_INVALID;
123     }
124 
125     return STATUS_IO_REPARSE_TAG_INVALID;
126 }
127