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)
23HRESULT
24MDFormat::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//*****************************************************************************
109PSTORAGESTREAM
110MDFormat::GetFirstStream(
111 PSTORAGEHEADER pHeader, // 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//*****************************************************************************
144PSTORAGESTREAM
145MDFormat::GetFirstStream_Verify(
146 PSTORAGEHEADER pHeader, // 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