VirtualBox

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

Last change on this file since 39080 was 33289, checked in by vboxsync, 14 years ago

Runtime;Main-OVF-Import: added online creation of SHA1 sums; preread/calc is done in a second worker thread; reading is cached; directly read out of an ova file; started to make reading fully streaming aware

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: manifest.cpp 33289 2010-10-21 10:00:15Z 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, const char * const *papszFiles, size_t cFiles,
199 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
200{
201 /* Validate input */
202 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
203 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
204 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
205
206 RTFILE file;
207 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL);
208 if (RT_FAILURE(rc))
209 return rc;
210
211 PRTMANIFESTTEST paFiles = 0;
212 void *pvBuf = 0;
213 do
214 {
215 paFiles = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * cFiles);
216 if (!paFiles)
217 {
218 rc = VERR_NO_MEMORY;
219 break;
220 }
221
222 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 };
223 for (size_t i = 0; i < cFiles; ++i)
224 {
225 paFiles[i].pszTestFile = papszFiles[i];
226 /* Calculate the SHA1 digest of every file */
227 if (pfnProgressCallback)
228 {
229 callback.cCurrentFile = i;
230 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, rtSHAProgressCallback, &callback);
231 }
232 else
233 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, NULL, NULL);
234 if (RT_FAILURE(rc))
235 break;
236 }
237
238 if (RT_SUCCESS(rc))
239 {
240 size_t cbSize = 0;
241 rc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, paFiles, cFiles);
242 if (RT_FAILURE(rc))
243 break;
244
245 rc = RTFileWrite(file, pvBuf, cbSize, 0);
246 }
247 }while (0);
248
249 RTFileClose(file);
250
251 /* Cleanup */
252 if (pvBuf)
253 RTMemFree(pvBuf);
254 for (size_t i = 0; i < cFiles; ++i)
255 if (paFiles[i].pszTestDigest)
256 RTStrFree((char*)paFiles[i].pszTestDigest);
257 RTMemFree(paFiles);
258
259 /* Delete the manifest file on failure */
260 if (RT_FAILURE(rc))
261 RTFileDelete(pszManifestFile);
262
263 return rc;
264}
265
266RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
267{
268 /* Validate input */
269 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
270 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
271 AssertPtrReturn(paTests, VERR_INVALID_POINTER);
272 AssertReturn(cTests > 0, VERR_INVALID_PARAMETER);
273 AssertPtrNullReturn(piFailed, VERR_INVALID_POINTER);
274
275 int rc = VINF_SUCCESS;
276
277 PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests);
278 if (!paFiles)
279 return VERR_NO_MEMORY;
280
281 /* Fill our compare list */
282 for (size_t i = 0; i < cTests; ++i)
283 paFiles[i].pTestPattern = &paTests[i];
284
285 char *pcBuf = (char*)pvBuf;
286 size_t cbRead = 0;
287 /* Parse the manifest file line by line */
288 for (;;)
289 {
290 if (cbRead >= cbSize)
291 break;
292
293 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1;
294
295 /* Skip empty lines (UNIX/DOS format) */
296 if ( ( cch == 1
297 && pcBuf[0] == '\n')
298 || ( cch == 2
299 && pcBuf[0] == '\r'
300 && pcBuf[1] == '\n'))
301 {
302 pcBuf += cch;
303 cbRead += cch;
304 continue;
305 }
306
307 /** @todo r=bird:
308 * -# Better deal with this EOF line platform dependency
309 * -# The SHA1 test should probably include a blank space check.
310 * -# If there is a specific order to the elements in the string, it would be
311 * good if the delimiter searching checked for it.
312 * -# Deal with filenames containing delimiter characters.
313 */
314
315 /* Check for the digest algorithm */
316 if ( cch < 4
317 || !( pcBuf[0] == 'S'
318 && pcBuf[1] == 'H'
319 && pcBuf[2] == 'A'
320 && pcBuf[3] == '1'))
321 {
322 /* Digest unsupported */
323 rc = VERR_MANIFEST_UNSUPPORTED_DIGEST_TYPE;
324 break;
325 }
326
327 /* Try to find the filename */
328 char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '(');
329 if (!pszNameStart)
330 {
331 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
332 break;
333 }
334 char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')');
335 if (!pszNameEnd)
336 {
337 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
338 break;
339 }
340
341 /* Copy the filename part */
342 size_t cchName = pszNameEnd - pszNameStart - 1;
343 char *pszName = (char *)RTMemTmpAlloc(cchName + 1);
344 if (!pszName)
345 {
346 rc = VERR_NO_MEMORY;
347 break;
348 }
349 memcpy(pszName, pszNameStart + 1, cchName);
350 pszName[cchName] = '\0';
351
352 /* Try to find the digest sum */
353 char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1;
354 if (!pszDigestStart)
355 {
356 RTMemTmpFree(pszName);
357 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
358 break;
359 }
360 char *pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\r');
361 if (!pszDigestEnd)
362 pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\n');
363 if (!pszDigestEnd)
364 {
365 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
366 break;
367 }
368 /* Copy the digest part */
369 size_t cchDigest = pszDigestEnd - pszDigestStart - 1;
370 char *pszDigest = (char *)RTMemTmpAlloc(cchDigest + 1);
371 if (!pszDigest)
372 {
373 rc = VERR_NO_MEMORY;
374 break;
375 }
376 memcpy(pszDigest, pszDigestStart + 1, cchDigest);
377 pszDigest[cchDigest] = '\0';
378
379 /* Check our file list against the extracted data */
380 bool fFound = false;
381 for (size_t i = 0; i < cTests; ++i)
382 {
383 if (!RTStrCmp(RTPathFilename(paFiles[i].pTestPattern->pszTestFile), RTStrStrip(pszName)))
384 {
385 /* Add the data of the manifest file to the file list */
386 paFiles[i].pszManifestFile = RTStrDup(RTStrStrip(pszName));
387 paFiles[i].pszManifestDigest = RTStrDup(RTStrStrip(pszDigest));
388 fFound = true;
389 break;
390 }
391 }
392 RTMemTmpFree(pszName);
393 RTMemTmpFree(pszDigest);
394 if (!fFound)
395 {
396 /* There have to be an entry in the file list */
397 rc = VERR_MANIFEST_FILE_MISMATCH;
398 break;
399 }
400
401 pcBuf += cch;
402 cbRead += cch;
403 }
404
405 if ( rc == VINF_SUCCESS
406 || rc == VERR_EOF)
407 {
408 rc = VINF_SUCCESS;
409 for (size_t i = 0; i < cTests; ++i)
410 {
411 /* If there is an entry in the file list, which hasn't an
412 * equivalent in the manifest file, its an error. */
413 if ( !paFiles[i].pszManifestFile
414 || !paFiles[i].pszManifestDigest)
415 {
416 rc = VERR_MANIFEST_FILE_MISMATCH;
417 break;
418 }
419
420 /* Do the manifest SHA1 digest match against the actual digest? */
421 if (RTStrICmp(paFiles[i].pszManifestDigest, paFiles[i].pTestPattern->pszTestDigest))
422 {
423 if (piFailed)
424 *piFailed = i;
425 rc = VERR_MANIFEST_DIGEST_MISMATCH;
426 break;
427 }
428 }
429 }
430
431 /* Cleanup */
432 for (size_t i = 0; i < cTests; ++i)
433 {
434 if (paFiles[i].pszManifestFile)
435 RTStrFree(paFiles[i].pszManifestFile);
436 if (paFiles[i].pszManifestDigest)
437 RTStrFree(paFiles[i].pszManifestDigest);
438 }
439 RTMemTmpFree(paFiles);
440
441 return rc;
442}
443
444RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, PRTMANIFESTTEST paFiles, size_t cFiles)
445{
446 /* Validate input */
447 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
448 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
449 AssertPtrReturn(paFiles, VERR_INVALID_POINTER);
450 AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER);
451
452 /* Calculate the size necessary for the memory buffer. */
453 size_t cbSize = 0;
454 size_t cbMaxSize = 0;
455 for (size_t i = 0; i < cFiles; ++i)
456 {
457 size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile)) + strlen(paFiles[i].pszTestDigest) + 10;
458 cbMaxSize = RT_MAX(cbMaxSize, cbTmp);
459 cbSize += cbTmp;
460 }
461
462 /* Create the memory buffer */
463 void *pvBuf = RTMemAlloc(cbSize);
464 if (!pvBuf)
465 return VERR_NO_MEMORY;
466
467 /* Allocate a temporary string buffer. */
468 char * pszTmp = RTStrAlloc(cbMaxSize + 1);
469 size_t cbPos = 0;
470 for (size_t i = 0; i < cFiles; ++i)
471 {
472 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "SHA1 (%s)= %s\n", RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest);
473 memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch);
474 cbPos += cch;
475 }
476 RTStrFree(pszTmp);
477
478 /* Results */
479 *ppvBuf = pvBuf;
480 *pcbSize = cbSize;
481
482 return VINF_SUCCESS;
483}
484
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