1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | // MDFileFormat.cpp |
6 | // |
7 | |
8 | // |
9 | // This file contains a set of helpers to verify and read the file format. |
10 | // This code does not handle the paging of the data, or different types of |
11 | // I/O. See the StgTiggerStorage and StgIO code for this level of support. |
12 | // |
13 | //***************************************************************************** |
14 | |
15 | #include "stdafx.h" // Standard header file. |
16 | #include "mdfileformat.h" // The format helpers. |
17 | #include "posterror.h" // Error handling code. |
18 | |
19 | //***************************************************************************** |
20 | // Verify the signature at the front of the file to see what type it is. |
21 | //***************************************************************************** |
22 | #define STORAGE_MAGIC_OLD_SIG 0x2B4D4F43 // +MOC (old version of BSJB signature code:STORAGE_MAGIC_SIG) |
23 | HRESULT |
24 | MDFormat::VerifySignature( |
25 | PSTORAGESIGNATURE pSig, // The signature to check. |
26 | ULONG cbData) |
27 | { |
28 | HRESULT hr = S_OK; |
29 | |
30 | // If signature didn't match, you shouldn't be here. |
31 | ULONG dwSignature = pSig->GetSignature(); |
32 | if (dwSignature == STORAGE_MAGIC_OLD_SIG) |
33 | { |
34 | Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC." ); |
35 | return PostError(CLDB_E_FILE_OLDVER, 1, 0); |
36 | } |
37 | if (dwSignature != STORAGE_MAGIC_SIG) |
38 | { |
39 | Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB." ); |
40 | return PostError(CLDB_E_FILE_CORRUPT); |
41 | } |
42 | |
43 | // Check for overflow |
44 | ULONG lVersionString = pSig->GetVersionStringLength(); |
45 | ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString; |
46 | if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString)) |
47 | { |
48 | Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow." ); |
49 | return PostError(CLDB_E_FILE_CORRUPT); |
50 | } |
51 | |
52 | // Check for invalid version string size |
53 | if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData) |
54 | { |
55 | Debug_ReportError("Invalid MetaData storage signature - version string too long." ); |
56 | return PostError(CLDB_E_FILE_CORRUPT); |
57 | } |
58 | |
59 | // Check that the version string is null terminated. This string |
60 | // is ANSI, so no double-null checks need to be made. |
61 | { |
62 | BYTE *pStart = &pSig->pVersion[0]; |
63 | BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL |
64 | BYTE *pCur; |
65 | |
66 | for (pCur = pStart; pCur < pEnd; pCur++) |
67 | { |
68 | if (*pCur == 0) |
69 | break; |
70 | } |
71 | |
72 | // If we got to the end without hitting a NULL, we have a bad version string |
73 | if (pCur == pEnd) |
74 | { |
75 | Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator." ); |
76 | return PostError(CLDB_E_FILE_CORRUPT); |
77 | } |
78 | } |
79 | |
80 | // Only a specific version of the 0.x format is supported by this code |
81 | // in order to support the NT 5 beta clients which used this format. |
82 | if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0) |
83 | { |
84 | if (pSig->GetMinorVer() < FILE_VER_MINOR_v0) |
85 | { |
86 | Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1." ); |
87 | hr = CLDB_E_FILE_OLDVER; |
88 | } |
89 | } |
90 | else |
91 | // There is currently no code to migrate an old format of the 1.x. This |
92 | // would be added only under special circumstances. |
93 | if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR)) |
94 | { |
95 | Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1." ); |
96 | hr = CLDB_E_FILE_OLDVER; |
97 | } |
98 | |
99 | if (FAILED(hr)) |
100 | hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer()); |
101 | return hr; |
102 | } // MDFormat::VerifySignature |
103 | |
104 | //***************************************************************************** |
105 | // Skip over the header and find the actual stream data. |
106 | // It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify |
107 | // instead. |
108 | //***************************************************************************** |
109 | PSTORAGESTREAM |
110 | MDFormat::( |
111 | PSTORAGEHEADER , // Return copy of header struct. |
112 | const void *pvMd) // Pointer to the full file. |
113 | { |
114 | const BYTE *pbMd; |
115 | |
116 | // Header data starts after signature. |
117 | pbMd = (const BYTE *) pvMd; |
118 | pbMd += sizeof(STORAGESIGNATURE); |
119 | pbMd += ((STORAGESIGNATURE*)pvMd)->GetVersionStringLength(); |
120 | PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; |
121 | *pHeader = *pHdr; |
122 | pbMd += sizeof(STORAGEHEADER); |
123 | |
124 | // ECMA specifies that the flags field is "reserved, must be 0". |
125 | if (pHdr->GetFlags() != 0) |
126 | return NULL; |
127 | |
128 | // The pointer is now at the first stream in the list. |
129 | return ((PSTORAGESTREAM) pbMd); |
130 | } // MDFormat::GetFirstStream |
131 | |
132 | //***************************************************************************** |
133 | // Skip over the header and find the actual stream data. Secure version of |
134 | // GetFirstStream method. |
135 | // The header is supposed to be verified by VerifySignature. |
136 | // |
137 | // Returns pointer to the first stream (behind storage header) and the size of |
138 | // the remaining buffer in *pcbMd (could be 0). |
139 | // Returns NULL if there is not enough buffer for reading the headers. The *pcbMd |
140 | // could be changed if NULL returned. |
141 | // |
142 | // Caller has to check available buffer size before using the first stream. |
143 | //***************************************************************************** |
144 | PSTORAGESTREAM |
145 | MDFormat::( |
146 | PSTORAGEHEADER , // Return copy of header struct. |
147 | const void *pvMd, // Pointer to the full file. |
148 | ULONG *pcbMd) // [in, out] Size of pvMd buffer (we don't want to read behind it) |
149 | { |
150 | const BYTE *pbMd; |
151 | |
152 | // Header data starts after signature. |
153 | pbMd = (const BYTE *)pvMd; |
154 | // Check read buffer overflow |
155 | if (*pcbMd < sizeof(STORAGESIGNATURE)) |
156 | { |
157 | Debug_ReportError("Invalid MetaData - Storage signature doesn't fit." ); |
158 | return NULL; |
159 | } |
160 | pbMd += sizeof(STORAGESIGNATURE); |
161 | *pcbMd -= sizeof(STORAGESIGNATURE); |
162 | |
163 | ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength(); |
164 | // Check read buffer overflow |
165 | if (*pcbMd < cbVersionString) |
166 | { |
167 | Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit." ); |
168 | return NULL; |
169 | } |
170 | pbMd += cbVersionString; |
171 | *pcbMd -= cbVersionString; |
172 | |
173 | // Is there enough space for storage header? |
174 | if (*pcbMd < sizeof(STORAGEHEADER)) |
175 | { |
176 | Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit." ); |
177 | return NULL; |
178 | } |
179 | PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; |
180 | *pHeader = *pHdr; |
181 | pbMd += sizeof(STORAGEHEADER); |
182 | *pcbMd -= sizeof(STORAGEHEADER); |
183 | |
184 | // ECMA specifies that the flags field is "reserved, must be 0". |
185 | if (pHdr->GetFlags() != 0) |
186 | { |
187 | Debug_ReportError("Invalid MetaData storage header - Flags are not 0." ); |
188 | return NULL; |
189 | } |
190 | |
191 | // The pointer is now at the first stream in the list. |
192 | return (PSTORAGESTREAM)pbMd; |
193 | } // MDFormat::GetFirstStream |
194 | |