VirtualBox

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

Last change on this file since 44529 was 44529, checked in by vboxsync, 12 years ago

header (C) fixes

  • 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 44529 2013-02-04 15:54:15Z vboxsync $ */
2/** @file
3 * IPRT - Manifest file handling.
4 */
5
6/*
7 * Copyright (C) 2009-2012 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 if (paFiles)
256 {
257 for (size_t i = 0; i < cFiles; ++i)
258 if (paFiles[i].pszTestDigest)
259 RTStrFree((char*)paFiles[i].pszTestDigest);
260 RTMemFree(paFiles);
261 }
262
263 /* Delete the manifest file on failure */
264 if (RT_FAILURE(rc))
265 RTFileDelete(pszManifestFile);
266
267 return rc;
268}
269
270RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
271{
272 /* Validate input */
273 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
274 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
275 AssertPtrReturn(paTests, VERR_INVALID_POINTER);
276 AssertReturn(cTests > 0, VERR_INVALID_PARAMETER);
277 AssertPtrNullReturn(piFailed, VERR_INVALID_POINTER);
278
279 int rc = VINF_SUCCESS;
280
281 PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests);
282 if (!paFiles)
283 return VERR_NO_MEMORY;
284
285 /* Fill our compare list */
286 for (size_t i = 0; i < cTests; ++i)
287 paFiles[i].pTestPattern = &paTests[i];
288
289 char *pcBuf = (char*)pvBuf;
290 size_t cbRead = 0;
291 /* Parse the manifest file line by line */
292 for (;;)
293 {
294 if (cbRead >= cbSize)
295 break;
296
297 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1;
298
299 /* Skip empty lines (UNIX/DOS format) */
300 if ( ( cch == 1
301 && pcBuf[0] == '\n')
302 || ( cch == 2
303 && pcBuf[0] == '\r'
304 && pcBuf[1] == '\n'))
305 {
306 pcBuf += cch;
307 cbRead += cch;
308 continue;
309 }
310
311 /** @todo r=bird:
312 * -# Better deal with this EOF line platform dependency
313 * -# The SHA1 test should probably include a blank space check.
314 * -# If there is a specific order to the elements in the string, it would be
315 * good if the delimiter searching checked for it.
316 * -# Deal with filenames containing delimiter characters.
317 */
318
319 /* Check for the digest algorithm */
320 if ( cch < 4
321 || !( pcBuf[0] == 'S'
322 && pcBuf[1] == 'H'
323 && pcBuf[2] == 'A'
324 && pcBuf[3] == '1'))
325 {
326 /* Digest unsupported */
327 rc = VERR_MANIFEST_UNSUPPORTED_DIGEST_TYPE;
328 break;
329 }
330
331 /* Try to find the filename */
332 char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '(');
333 if (!pszNameStart)
334 {
335 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
336 break;
337 }
338 char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')');
339 if (!pszNameEnd)
340 {
341 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
342 break;
343 }
344
345 /* Copy the filename part */
346 size_t cchName = pszNameEnd - pszNameStart - 1;
347 char *pszName = (char *)RTMemTmpAlloc(cchName + 1);
348 if (!pszName)
349 {
350 rc = VERR_NO_MEMORY;
351 break;
352 }
353 memcpy(pszName, pszNameStart + 1, cchName);
354 pszName[cchName] = '\0';
355
356 /* Try to find the digest sum */
357 char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1;
358 if (!pszDigestStart)
359 {
360 RTMemTmpFree(pszName);
361 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
362 break;
363 }
364 char *pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\r');
365 if (!pszDigestEnd)
366 pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\n');
367 if (!pszDigestEnd)
368 {
369 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
370 break;
371 }
372 /* Copy the digest part */
373 size_t cchDigest = pszDigestEnd - pszDigestStart - 1;
374 char *pszDigest = (char *)RTMemTmpAlloc(cchDigest + 1);
375 if (!pszDigest)
376 {
377 rc = VERR_NO_MEMORY;
378 break;
379 }
380 memcpy(pszDigest, pszDigestStart + 1, cchDigest);
381 pszDigest[cchDigest] = '\0';
382
383 /* Check our file list against the extracted data */
384 bool fFound = false;
385 for (size_t i = 0; i < cTests; ++i)
386 {
387 if (!RTStrCmp(RTPathFilename(paFiles[i].pTestPattern->pszTestFile), RTStrStrip(pszName)))
388 {
389 /* Add the data of the manifest file to the file list */
390 paFiles[i].pszManifestFile = RTStrDup(RTStrStrip(pszName));
391 paFiles[i].pszManifestDigest = RTStrDup(RTStrStrip(pszDigest));
392 fFound = true;
393 break;
394 }
395 }
396 RTMemTmpFree(pszName);
397 RTMemTmpFree(pszDigest);
398 if (!fFound)
399 {
400 /* There have to be an entry in the file list */
401 rc = VERR_MANIFEST_FILE_MISMATCH;
402 break;
403 }
404
405 pcBuf += cch;
406 cbRead += cch;
407 }
408
409 if ( rc == VINF_SUCCESS
410 || rc == VERR_EOF)
411 {
412 rc = VINF_SUCCESS;
413 for (size_t i = 0; i < cTests; ++i)
414 {
415 /* If there is an entry in the file list, which hasn't an
416 * equivalent in the manifest file, its an error. */
417 if ( !paFiles[i].pszManifestFile
418 || !paFiles[i].pszManifestDigest)
419 {
420 rc = VERR_MANIFEST_FILE_MISMATCH;
421 break;
422 }
423
424 /* Do the manifest SHA1 digest match against the actual digest? */
425 if (RTStrICmp(paFiles[i].pszManifestDigest, paFiles[i].pTestPattern->pszTestDigest))
426 {
427 if (piFailed)
428 *piFailed = i;
429 rc = VERR_MANIFEST_DIGEST_MISMATCH;
430 break;
431 }
432 }
433 }
434
435 /* Cleanup */
436 for (size_t i = 0; i < cTests; ++i)
437 {
438 if (paFiles[i].pszManifestFile)
439 RTStrFree(paFiles[i].pszManifestFile);
440 if (paFiles[i].pszManifestDigest)
441 RTStrFree(paFiles[i].pszManifestDigest);
442 }
443 RTMemTmpFree(paFiles);
444
445 RTPrintf("rc = %Rrc\n", rc);
446 return rc;
447}
448
449RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, RTDIGESTTYPE enmDigestType, PRTMANIFESTTEST paFiles, size_t cFiles)
450{
451 /* Validate input */
452 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
453 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
454 AssertPtrReturn(paFiles, VERR_INVALID_POINTER);
455 AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER);
456
457 const char *pcszDigestType;
458 switch (enmDigestType)
459 {
460 case RTDIGESTTYPE_CRC32: pcszDigestType = "CRC32"; break;
461 case RTDIGESTTYPE_CRC64: pcszDigestType = "CRC64"; break;
462 case RTDIGESTTYPE_MD5: pcszDigestType = "MD5"; break;
463 case RTDIGESTTYPE_SHA1: pcszDigestType = "SHA1"; break;
464 case RTDIGESTTYPE_SHA256: pcszDigestType = "SHA256"; break;
465 default: return VERR_INVALID_PARAMETER;
466 }
467
468 /* Calculate the size necessary for the memory buffer. */
469 size_t cbSize = 0;
470 size_t cbMaxSize = 0;
471 for (size_t i = 0; i < cFiles; ++i)
472 {
473 size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile))
474 + strlen(paFiles[i].pszTestDigest)
475 + strlen(pcszDigestType)
476 + 6;
477 cbMaxSize = RT_MAX(cbMaxSize, cbTmp);
478 cbSize += cbTmp;
479 }
480
481 /* Create the memory buffer */
482 void *pvBuf = RTMemAlloc(cbSize);
483 if (!pvBuf)
484 return VERR_NO_MEMORY;
485
486 /* Allocate a temporary string buffer. */
487 char *pszTmp = RTStrAlloc(cbMaxSize + 1);
488 if (!pszTmp)
489 {
490 RTMemFree(pvBuf);
491 return VERR_NO_MEMORY;
492 }
493 size_t cbPos = 0;
494
495 for (size_t i = 0; i < cFiles; ++i)
496 {
497 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "%s (%s)= %s\n", pcszDigestType, RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest);
498 memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch);
499 cbPos += cch;
500 }
501 RTStrFree(pszTmp);
502
503 /* Results */
504 *ppvBuf = pvBuf;
505 *pcbSize = cbSize;
506
507 return VINF_SUCCESS;
508}
509
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