VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/checksum/manifest.cpp@ 42072

Last change on this file since 42072 was 41170, checked in by vboxsync, 13 years ago

fixed tstRTManifest testcase

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: manifest.cpp 41170 2012-05-04 14:58:29Z vboxsync $ */
2/** @file
3 * IPRT - Manifest file handling.
4 */
5
6/*
7 * Copyright (C) 2009 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/manifest.h>
33
34#include <iprt/err.h>
35#include <iprt/file.h>
36#include <iprt/mem.h>
37#include <iprt/path.h>
38#include <iprt/sha.h>
39#include <iprt/stream.h>
40#include <iprt/string.h>
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/**
47 * Internal per file structure used by RTManifestVerify
48 */
49typedef struct RTMANIFESTFILEENTRY
50{
51 char *pszManifestFile;
52 char *pszManifestDigest;
53 PRTMANIFESTTEST pTestPattern;
54} RTMANIFESTFILEENTRY;
55typedef RTMANIFESTFILEENTRY* PRTMANIFESTFILEENTRY;
56
57/**
58 * Internal structure used for the progress callback
59 */
60typedef struct RTMANIFESTCALLBACKDATA
61{
62 PFNRTPROGRESS pfnProgressCallback;
63 void *pvUser;
64 size_t cMaxFiles;
65 size_t cCurrentFile;
66} RTMANIFESTCALLBACKDATA;
67typedef RTMANIFESTCALLBACKDATA* PRTMANIFESTCALLBACKDATA;
68
69/*******************************************************************************
70* Private functions
71*******************************************************************************/
72
73DECLINLINE(char *) rtManifestPosOfCharInBuf(char const *pv, size_t cb, char c)
74{
75 char *pb = (char *)pv;
76 for (; cb; --cb, ++pb)
77 if (RT_UNLIKELY(*pb == c))
78 return pb;
79 return NULL;
80}
81
82DECLINLINE(size_t) rtManifestIndexOfCharInBuf(char const *pv, size_t cb, char c)
83{
84 char const *pb = (char const *)pv;
85 for (size_t i=0; i < cb; ++i, ++pb)
86 if (RT_UNLIKELY(*pb == c))
87 return i;
88 return cb;
89}
90
91int rtSHAProgressCallback(unsigned uPercent, void *pvUser)
92{
93 PRTMANIFESTCALLBACKDATA pData = (PRTMANIFESTCALLBACKDATA)pvUser;
94 return pData->pfnProgressCallback((unsigned)( (uPercent + (float)pData->cCurrentFile * 100.0)
95 / (float)pData->cMaxFiles),
96 pData->pvUser);
97}
98
99/*******************************************************************************
100* Public functions
101*******************************************************************************/
102
103RTR3DECL(int) RTManifestVerify(const char *pszManifestFile, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
104{
105 /* Validate input */
106 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
107
108 /* Open the manifest file */
109 RTFILE file;
110 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
111 if (RT_FAILURE(rc))
112 return rc;
113
114 void *pvBuf = 0;
115 do
116 {
117 uint64_t cbSize;
118 rc = RTFileGetSize(file, &cbSize);
119 if (RT_FAILURE(rc))
120 break;
121
122 /* Cast down for the case size_t < uint64_t. This isn't really correct,
123 but we consider manifest files bigger than size_t as not supported
124 by now. */
125 size_t cbToRead = (size_t)cbSize;
126 pvBuf = RTMemAlloc(cbToRead);
127 if (!pvBuf)
128 {
129 rc = VERR_NO_MEMORY;
130 break;
131 }
132
133 size_t cbRead = 0;
134 rc = RTFileRead(file, pvBuf, cbToRead, &cbRead);
135 if (RT_FAILURE(rc))
136 break;
137
138 rc = RTManifestVerifyFilesBuf(pvBuf, cbRead, paTests, cTests, piFailed);
139 }while (0);
140
141 /* Cleanup */
142 if (pvBuf)
143 RTMemFree(pvBuf);
144
145 RTFileClose(file);
146
147 return rc;
148}
149
150RTR3DECL(int) RTManifestVerifyFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, size_t *piFailed,
151 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
152{
153 /* Validate input */
154 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
155 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
156 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
157
158 int rc = VINF_SUCCESS;
159
160 /* Create our compare list */
161 PRTMANIFESTTEST paFiles = (PRTMANIFESTTEST)RTMemTmpAllocZ(sizeof(RTMANIFESTTEST) * cFiles);
162 if (!paFiles)
163 return VERR_NO_MEMORY;
164
165 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 };
166 /* Fill our compare list */
167 for (size_t i = 0; i < cFiles; ++i)
168 {
169 char *pszDigest;
170 if (pfnProgressCallback)
171 {
172 callback.cCurrentFile = i;
173 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, rtSHAProgressCallback, &callback);
174 }
175 else
176 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, NULL, NULL);
177 if (RT_FAILURE(rc))
178 break;
179 paFiles[i].pszTestFile = (char*)papszFiles[i];
180 paFiles[i].pszTestDigest = pszDigest;
181 }
182
183 /* Do the verification */
184 if (RT_SUCCESS(rc))
185 rc = RTManifestVerify(pszManifestFile, paFiles, cFiles, piFailed);
186
187 /* Cleanup */
188 for (size_t i = 0; i < cFiles; ++i)
189 {
190 if (paFiles[i].pszTestDigest)
191 RTStrFree((char*)paFiles[i].pszTestDigest);
192 }
193 RTMemTmpFree(paFiles);
194
195 return rc;
196}
197
198RTR3DECL(int) RTManifestWriteFiles(const char *pszManifestFile, RTDIGESTTYPE enmDigestType,
199 const char * const *papszFiles, size_t cFiles,
200 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
201{
202 /* Validate input */
203 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
204 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
205 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
206
207 RTFILE file;
208 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL);
209 if (RT_FAILURE(rc))
210 return rc;
211
212 PRTMANIFESTTEST paFiles = 0;
213 void *pvBuf = 0;
214 do
215 {
216 paFiles = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * cFiles);
217 if (!paFiles)
218 {
219 rc = VERR_NO_MEMORY;
220 break;
221 }
222
223 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 };
224 for (size_t i = 0; i < cFiles; ++i)
225 {
226 paFiles[i].pszTestFile = papszFiles[i];
227 /* Calculate the SHA1 digest of every file */
228 if (pfnProgressCallback)
229 {
230 callback.cCurrentFile = i;
231 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, rtSHAProgressCallback, &callback);
232 }
233 else
234 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, NULL, NULL);
235 if (RT_FAILURE(rc))
236 break;
237 }
238
239 if (RT_SUCCESS(rc))
240 {
241 size_t cbSize = 0;
242 rc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, enmDigestType, paFiles, cFiles);
243 if (RT_FAILURE(rc))
244 break;
245
246 rc = RTFileWrite(file, pvBuf, cbSize, 0);
247 }
248 }while (0);
249
250 RTFileClose(file);
251
252 /* Cleanup */
253 if (pvBuf)
254 RTMemFree(pvBuf);
255 for (size_t i = 0; i < cFiles; ++i)
256 if (paFiles[i].pszTestDigest)
257 RTStrFree((char*)paFiles[i].pszTestDigest);
258 RTMemFree(paFiles);
259
260 /* Delete the manifest file on failure */
261 if (RT_FAILURE(rc))
262 RTFileDelete(pszManifestFile);
263
264 return rc;
265}
266
267RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
268{
269 /* Validate input */
270 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
271 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
272 AssertPtrReturn(paTests, VERR_INVALID_POINTER);
273 AssertReturn(cTests > 0, VERR_INVALID_PARAMETER);
274 AssertPtrNullReturn(piFailed, VERR_INVALID_POINTER);
275
276 int rc = VINF_SUCCESS;
277
278 PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests);
279 if (!paFiles)
280 return VERR_NO_MEMORY;
281
282 /* Fill our compare list */
283 for (size_t i = 0; i < cTests; ++i)
284 paFiles[i].pTestPattern = &paTests[i];
285
286 char *pcBuf = (char*)pvBuf;
287 size_t cbRead = 0;
288 /* Parse the manifest file line by line */
289 for (;;)
290 {
291 if (cbRead >= cbSize)
292 break;
293
294 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1;
295
296 /* Skip empty lines (UNIX/DOS format) */
297 if ( ( cch == 1
298 && pcBuf[0] == '\n')
299 || ( cch == 2
300 && pcBuf[0] == '\r'
301 && pcBuf[1] == '\n'))
302 {
303 pcBuf += cch;
304 cbRead += cch;
305 continue;
306 }
307
308 /** @todo r=bird:
309 * -# Better deal with this EOF line platform dependency
310 * -# The SHA1 test should probably include a blank space check.
311 * -# If there is a specific order to the elements in the string, it would be
312 * good if the delimiter searching checked for it.
313 * -# Deal with filenames containing delimiter characters.
314 */
315
316 /* Check for the digest algorithm */
317 if ( cch < 4
318 || !( pcBuf[0] == 'S'
319 && pcBuf[1] == 'H'
320 && pcBuf[2] == 'A'
321 && pcBuf[3] == '1'))
322 {
323 /* Digest unsupported */
324 rc = VERR_MANIFEST_UNSUPPORTED_DIGEST_TYPE;
325 break;
326 }
327
328 /* Try to find the filename */
329 char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '(');
330 if (!pszNameStart)
331 {
332 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
333 break;
334 }
335 char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')');
336 if (!pszNameEnd)
337 {
338 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
339 break;
340 }
341
342 /* Copy the filename part */
343 size_t cchName = pszNameEnd - pszNameStart - 1;
344 char *pszName = (char *)RTMemTmpAlloc(cchName + 1);
345 if (!pszName)
346 {
347 rc = VERR_NO_MEMORY;
348 break;
349 }
350 memcpy(pszName, pszNameStart + 1, cchName);
351 pszName[cchName] = '\0';
352
353 /* Try to find the digest sum */
354 char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1;
355 if (!pszDigestStart)
356 {
357 RTMemTmpFree(pszName);
358 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
359 break;
360 }
361 char *pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\r');
362 if (!pszDigestEnd)
363 pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\n');
364 if (!pszDigestEnd)
365 {
366 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
367 break;
368 }
369 /* Copy the digest part */
370 size_t cchDigest = pszDigestEnd - pszDigestStart - 1;
371 char *pszDigest = (char *)RTMemTmpAlloc(cchDigest + 1);
372 if (!pszDigest)
373 {
374 rc = VERR_NO_MEMORY;
375 break;
376 }
377 memcpy(pszDigest, pszDigestStart + 1, cchDigest);
378 pszDigest[cchDigest] = '\0';
379
380 /* Check our file list against the extracted data */
381 bool fFound = false;
382 for (size_t i = 0; i < cTests; ++i)
383 {
384 if (!RTStrCmp(RTPathFilename(paFiles[i].pTestPattern->pszTestFile), RTStrStrip(pszName)))
385 {
386 /* Add the data of the manifest file to the file list */
387 paFiles[i].pszManifestFile = RTStrDup(RTStrStrip(pszName));
388 paFiles[i].pszManifestDigest = RTStrDup(RTStrStrip(pszDigest));
389 fFound = true;
390 break;
391 }
392 }
393 RTMemTmpFree(pszName);
394 RTMemTmpFree(pszDigest);
395 if (!fFound)
396 {
397 /* There have to be an entry in the file list */
398 rc = VERR_MANIFEST_FILE_MISMATCH;
399 break;
400 }
401
402 pcBuf += cch;
403 cbRead += cch;
404 }
405
406 if ( rc == VINF_SUCCESS
407 || rc == VERR_EOF)
408 {
409 rc = VINF_SUCCESS;
410 for (size_t i = 0; i < cTests; ++i)
411 {
412 /* If there is an entry in the file list, which hasn't an
413 * equivalent in the manifest file, its an error. */
414 if ( !paFiles[i].pszManifestFile
415 || !paFiles[i].pszManifestDigest)
416 {
417 rc = VERR_MANIFEST_FILE_MISMATCH;
418 break;
419 }
420
421 /* Do the manifest SHA1 digest match against the actual digest? */
422 if (RTStrICmp(paFiles[i].pszManifestDigest, paFiles[i].pTestPattern->pszTestDigest))
423 {
424 if (piFailed)
425 *piFailed = i;
426 rc = VERR_MANIFEST_DIGEST_MISMATCH;
427 break;
428 }
429 }
430 }
431
432 /* Cleanup */
433 for (size_t i = 0; i < cTests; ++i)
434 {
435 if (paFiles[i].pszManifestFile)
436 RTStrFree(paFiles[i].pszManifestFile);
437 if (paFiles[i].pszManifestDigest)
438 RTStrFree(paFiles[i].pszManifestDigest);
439 }
440 RTMemTmpFree(paFiles);
441
442 RTPrintf("rc = %Rrc\n", rc);
443 return rc;
444}
445
446RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, RTDIGESTTYPE enmDigestType, PRTMANIFESTTEST paFiles, size_t cFiles)
447{
448 /* Validate input */
449 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
450 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
451 AssertPtrReturn(paFiles, VERR_INVALID_POINTER);
452 AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER);
453
454 const char *pcszDigestType;
455 switch (enmDigestType)
456 {
457 case RTDIGESTTYPE_CRC32: pcszDigestType = "CRC32"; break;
458 case RTDIGESTTYPE_CRC64: pcszDigestType = "CRC64"; break;
459 case RTDIGESTTYPE_MD5: pcszDigestType = "MD5"; break;
460 case RTDIGESTTYPE_SHA1: pcszDigestType = "SHA1"; break;
461 case RTDIGESTTYPE_SHA256: pcszDigestType = "SHA256"; break;
462 default: return VERR_INVALID_PARAMETER;
463 }
464
465 /* Calculate the size necessary for the memory buffer. */
466 size_t cbSize = 0;
467 size_t cbMaxSize = 0;
468 for (size_t i = 0; i < cFiles; ++i)
469 {
470 size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile))
471 + strlen(paFiles[i].pszTestDigest)
472 + strlen(pcszDigestType)
473 + 6;
474 cbMaxSize = RT_MAX(cbMaxSize, cbTmp);
475 cbSize += cbTmp;
476 }
477
478 /* Create the memory buffer */
479 void *pvBuf = RTMemAlloc(cbSize);
480 if (!pvBuf)
481 return VERR_NO_MEMORY;
482
483 /* Allocate a temporary string buffer. */
484 char *pszTmp = RTStrAlloc(cbMaxSize + 1);
485 if (!pszTmp)
486 {
487 RTMemFree(pvBuf);
488 return VERR_NO_MEMORY;
489 }
490 size_t cbPos = 0;
491
492 for (size_t i = 0; i < cFiles; ++i)
493 {
494 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "%s (%s)= %s\n", pcszDigestType, RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest);
495 memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch);
496 cbPos += cch;
497 }
498 RTStrFree(pszTmp);
499
500 /* Results */
501 *ppvBuf = pvBuf;
502 *pcbSize = cbSize;
503
504 return VINF_SUCCESS;
505}
506
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette