VirtualBox

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

Last change on this file since 81106 was 80585, checked in by vboxsync, 5 years ago

Runtime: Some renaming to stay consistent (*Get* always returns what is asked for while *Query* returns a status code and where to store the value on success is given as a pointer)

  • RTVfsFileGetSize -> RTVfsFileQuerySize
  • RTFileQuerySize -> RTFileQuerySizeByPath
  • RTFileGetSize -> RTFileQuerySize
  • RTFileGetSizeMaxEx -> RTFileQuerySizeMaxEx
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 KB
Line 
1/* $Id: manifest.cpp 80585 2019-09-04 14:05:50Z vboxsync $ */
2/** @file
3 * IPRT - Manifest file handling, old style - deprecated.
4 */
5
6/*
7 * Copyright (C) 2009-2019 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/*******************************************************************************
71* Private functions
72*******************************************************************************/
73
74DECLINLINE(char *) rtManifestPosOfCharInBuf(char const *pv, size_t cb, char c)
75{
76 char *pb = (char *)pv;
77 for (; cb; --cb, ++pb)
78 if (RT_UNLIKELY(*pb == c))
79 return pb;
80 return NULL;
81}
82
83DECLINLINE(size_t) rtManifestIndexOfCharInBuf(char const *pv, size_t cb, char c)
84{
85 char const *pb = (char const *)pv;
86 for (size_t i=0; i < cb; ++i, ++pb)
87 if (RT_UNLIKELY(*pb == c))
88 return i;
89 return cb;
90}
91
92static DECLCALLBACK(int) rtSHAProgressCallback(unsigned uPercent, void *pvUser)
93{
94 PRTMANIFESTCALLBACKDATA pData = (PRTMANIFESTCALLBACKDATA)pvUser;
95 return pData->pfnProgressCallback((unsigned)( (uPercent + (float)pData->cCurrentFile * 100.0)
96 / (float)pData->cMaxFiles),
97 pData->pvUser);
98}
99
100
101/*******************************************************************************
102* Public functions
103*******************************************************************************/
104
105RTR3DECL(int) RTManifestVerify(const char *pszManifestFile, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
106{
107 /* Validate input */
108 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
109
110 /* Open the manifest file */
111 RTFILE file;
112 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
113 if (RT_FAILURE(rc))
114 return rc;
115
116 void *pvBuf = 0;
117 do
118 {
119 uint64_t cbSize;
120 rc = RTFileQuerySize(file, &cbSize);
121 if (RT_FAILURE(rc))
122 break;
123
124 /* Cast down for the case size_t < uint64_t. This isn't really correct,
125 but we consider manifest files bigger than size_t as not supported
126 by now. */
127 size_t cbToRead = (size_t)cbSize;
128 pvBuf = RTMemAlloc(cbToRead);
129 if (!pvBuf)
130 {
131 rc = VERR_NO_MEMORY;
132 break;
133 }
134
135 size_t cbRead = 0;
136 rc = RTFileRead(file, pvBuf, cbToRead, &cbRead);
137 if (RT_FAILURE(rc))
138 break;
139
140 rc = RTManifestVerifyFilesBuf(pvBuf, cbRead, paTests, cTests, piFailed);
141 }while (0);
142
143 /* Cleanup */
144 if (pvBuf)
145 RTMemFree(pvBuf);
146
147 RTFileClose(file);
148
149 return rc;
150}
151
152RTR3DECL(int) RTManifestVerifyFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, size_t *piFailed,
153 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
154{
155 /* Validate input */
156 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
157 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
158 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
159
160 int rc = VINF_SUCCESS;
161
162 /* Create our compare list */
163 PRTMANIFESTTEST paFiles = (PRTMANIFESTTEST)RTMemTmpAllocZ(sizeof(RTMANIFESTTEST) * cFiles);
164 if (!paFiles)
165 return VERR_NO_MEMORY;
166
167 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 };
168 /* Fill our compare list */
169 for (size_t i = 0; i < cFiles; ++i)
170 {
171 char *pszDigest;
172 if (pfnProgressCallback)
173 {
174 callback.cCurrentFile = i;
175 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, rtSHAProgressCallback, &callback);
176 }
177 else
178 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, NULL, NULL);
179 if (RT_FAILURE(rc))
180 break;
181 paFiles[i].pszTestFile = (char*)papszFiles[i];
182 paFiles[i].pszTestDigest = pszDigest;
183 }
184
185 /* Do the verification */
186 if (RT_SUCCESS(rc))
187 rc = RTManifestVerify(pszManifestFile, paFiles, cFiles, piFailed);
188
189 /* Cleanup */
190 for (size_t i = 0; i < cFiles; ++i)
191 {
192 if (paFiles[i].pszTestDigest)
193 RTStrFree((char*)paFiles[i].pszTestDigest);
194 }
195 RTMemTmpFree(paFiles);
196
197 return rc;
198}
199
200RTR3DECL(int) RTManifestWriteFiles(const char *pszManifestFile, RTDIGESTTYPE enmDigestType,
201 const char * const *papszFiles, size_t cFiles,
202 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
203{
204 /* Validate input */
205 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER);
206 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
207 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
208
209 RTFILE file;
210 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL);
211 if (RT_FAILURE(rc))
212 return rc;
213
214 PRTMANIFESTTEST paFiles = 0;
215 void *pvBuf = 0;
216 do
217 {
218 paFiles = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * cFiles);
219 if (!paFiles)
220 {
221 rc = VERR_NO_MEMORY;
222 break;
223 }
224
225 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 };
226 for (size_t i = 0; i < cFiles; ++i)
227 {
228 paFiles[i].pszTestFile = papszFiles[i];
229 /* Calculate the SHA1 digest of every file */
230 if (pfnProgressCallback)
231 {
232 callback.cCurrentFile = i;
233 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, rtSHAProgressCallback, &callback);
234 }
235 else
236 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, NULL, NULL);
237 if (RT_FAILURE(rc))
238 break;
239 }
240
241 if (RT_SUCCESS(rc))
242 {
243 size_t cbSize = 0;
244 rc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, enmDigestType, paFiles, cFiles);
245 if (RT_FAILURE(rc))
246 break;
247
248 rc = RTFileWrite(file, pvBuf, cbSize, 0);
249 }
250 }while (0);
251
252 RTFileClose(file);
253
254 /* Cleanup */
255 if (pvBuf)
256 RTMemFree(pvBuf);
257 if (paFiles)
258 {
259 for (size_t i = 0; i < cFiles; ++i)
260 if (paFiles[i].pszTestDigest)
261 RTStrFree((char*)paFiles[i].pszTestDigest);
262 RTMemFree(paFiles);
263 }
264
265 /* Delete the manifest file on failure */
266 if (RT_FAILURE(rc))
267 RTFileDelete(pszManifestFile);
268
269 return rc;
270}
271
272
273RTR3DECL(int) RTManifestVerifyDigestType(void const *pvBuf, size_t cbSize, RTDIGESTTYPE *penmDigestType)
274{
275 /* Validate input */
276 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
277 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
278 AssertPtrReturn(penmDigestType, VERR_INVALID_POINTER);
279
280 int rc = VINF_SUCCESS;
281
282 char const *pcBuf = (char *)pvBuf;
283 size_t cbRead = 0;
284 /* Parse the manifest file line by line */
285 for (;;)
286 {
287 if (cbRead >= cbSize)
288 return VERR_MANIFEST_UNSUPPORTED_DIGEST_TYPE;
289
290 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1;
291
292 /* Skip empty lines (UNIX/DOS format) */
293 if ( ( cch == 1
294 && pcBuf[0] == '\n')
295 || ( cch == 2
296 && pcBuf[0] == '\r'
297 && pcBuf[1] == '\n'))
298 {
299 pcBuf += cch;
300 cbRead += cch;
301 continue;
302 }
303
304/** @todo r=bird: Missing space check here. */
305 /* Check for the digest algorithm */
306 if ( pcBuf[0] == 'S'
307 && pcBuf[1] == 'H'
308 && pcBuf[2] == 'A'
309 && pcBuf[3] == '1')
310 {
311 *penmDigestType = RTDIGESTTYPE_SHA1;
312 break;
313 }
314 if ( pcBuf[0] == 'S'
315 && pcBuf[1] == 'H'
316 && pcBuf[2] == 'A'
317 && pcBuf[3] == '2'
318 && pcBuf[4] == '5'
319 && pcBuf[5] == '6')
320 {
321 *penmDigestType = RTDIGESTTYPE_SHA256;
322 break;
323 }
324
325 pcBuf += cch;
326 cbRead += cch;
327 }
328
329 return rc;
330}
331
332
333RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
334{
335 /* Validate input */
336 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
337 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER);
338 AssertPtrReturn(paTests, VERR_INVALID_POINTER);
339 AssertReturn(cTests > 0, VERR_INVALID_PARAMETER);
340 AssertPtrNullReturn(piFailed, VERR_INVALID_POINTER);
341
342 int rc = VINF_SUCCESS;
343
344 PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests);
345 if (!paFiles)
346 return VERR_NO_MEMORY;
347
348 /* Fill our compare list */
349 for (size_t i = 0; i < cTests; ++i)
350 paFiles[i].pTestPattern = &paTests[i];
351
352 char *pcBuf = (char*)pvBuf;
353 size_t cbRead = 0;
354 /* Parse the manifest file line by line */
355 for (;;)
356 {
357 if (cbRead >= cbSize)
358 break;
359
360 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1;
361
362 /* Skip empty lines (UNIX/DOS format) */
363 if ( ( cch == 1
364 && pcBuf[0] == '\n')
365 || ( cch == 2
366 && pcBuf[0] == '\r'
367 && pcBuf[1] == '\n'))
368 {
369 pcBuf += cch;
370 cbRead += cch;
371 continue;
372 }
373
374 /** @todo r=bird:
375 * -# Better deal with this EOF line platform dependency
376 * -# The SHA1 and SHA256 tests should probably include a blank space check.
377 * -# If there is a specific order to the elements in the string, it would be
378 * good if the delimiter searching checked for it.
379 * -# Deal with filenames containing delimiter characters.
380 */
381
382 /* Check for the digest algorithm */
383 if ( cch < 4
384 || ( !( pcBuf[0] == 'S'
385 && pcBuf[1] == 'H'
386 && pcBuf[2] == 'A'
387 && pcBuf[3] == '1')
388 &&
389 !( pcBuf[0] == 'S'
390 && pcBuf[1] == 'H'
391 && pcBuf[2] == 'A'
392 && pcBuf[3] == '2'
393 && pcBuf[4] == '5'
394 && pcBuf[5] == '6')
395 )
396 )
397 {
398 /* Digest unsupported */
399 rc = VERR_MANIFEST_UNSUPPORTED_DIGEST_TYPE;
400 break;
401 }
402
403 /* Try to find the filename */
404 char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '(');
405 if (!pszNameStart)
406 {
407 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
408 break;
409 }
410 char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')');
411 if (!pszNameEnd)
412 {
413 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
414 break;
415 }
416
417 /* Copy the filename part */
418 size_t cchName = pszNameEnd - pszNameStart - 1;
419 char *pszName = (char *)RTMemTmpAlloc(cchName + 1);
420 if (!pszName)
421 {
422 rc = VERR_NO_MEMORY;
423 break;
424 }
425 memcpy(pszName, pszNameStart + 1, cchName);
426 pszName[cchName] = '\0';
427
428 /* Try to find the digest sum */
429 char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1;
430 if (!pszDigestStart)
431 {
432 RTMemTmpFree(pszName);
433 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
434 break;
435 }
436 char *pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\r');
437 if (!pszDigestEnd)
438 pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\n');
439 if (!pszDigestEnd)
440 {
441 RTMemTmpFree(pszName);
442 rc = VERR_MANIFEST_WRONG_FILE_FORMAT;
443 break;
444 }
445 /* Copy the digest part */
446 size_t cchDigest = pszDigestEnd - pszDigestStart - 1;
447 char *pszDigest = (char *)RTMemTmpAlloc(cchDigest + 1);
448 if (!pszDigest)
449 {
450 RTMemTmpFree(pszName);
451 rc = VERR_NO_MEMORY;
452 break;
453 }
454 memcpy(pszDigest, pszDigestStart + 1, cchDigest);
455 pszDigest[cchDigest] = '\0';
456
457 /* Check our file list against the extracted data */
458 bool fFound = false;
459 for (size_t i = 0; i < cTests; ++i)
460 {
461 /** @todo r=bird: Using RTStrStr here looks bogus. */
462 if (RTStrStr(paFiles[i].pTestPattern->pszTestFile, RTStrStrip(pszName)) != NULL)
463 {
464 /* Add the data of the manifest file to the file list */
465 paFiles[i].pszManifestFile = RTStrDup(RTStrStrip(pszName));
466 paFiles[i].pszManifestDigest = RTStrDup(RTStrStrip(pszDigest));
467 fFound = true;
468 break;
469 }
470 }
471 RTMemTmpFree(pszName);
472 RTMemTmpFree(pszDigest);
473 if (!fFound)
474 {
475 /* There have to be an entry in the file list */
476 rc = VERR_MANIFEST_FILE_MISMATCH;
477 break;
478 }
479
480 pcBuf += cch;
481 cbRead += cch;
482 }
483
484 if ( rc == VINF_SUCCESS
485 || rc == VERR_EOF)
486 {
487 rc = VINF_SUCCESS;
488 for (size_t i = 0; i < cTests; ++i)
489 {
490 /* If there is an entry in the file list, which hasn't an
491 * equivalent in the manifest file, its an error. */
492 if ( !paFiles[i].pszManifestFile
493 || !paFiles[i].pszManifestDigest)
494 {
495 rc = VERR_MANIFEST_FILE_MISMATCH;
496 break;
497 }
498
499 /* Do the manifest SHA digest match against the actual digest? */
500 if (RTStrICmp(paFiles[i].pszManifestDigest, paFiles[i].pTestPattern->pszTestDigest))
501 {
502 if (piFailed)
503 *piFailed = i;
504 rc = VERR_MANIFEST_DIGEST_MISMATCH;
505 break;
506 }
507 }
508 }
509
510 /* Cleanup */
511 for (size_t i = 0; i < cTests; ++i)
512 {
513 if (paFiles[i].pszManifestFile)
514 RTStrFree(paFiles[i].pszManifestFile);
515 if (paFiles[i].pszManifestDigest)
516 RTStrFree(paFiles[i].pszManifestDigest);
517 }
518 RTMemTmpFree(paFiles);
519
520 return rc;
521}
522
523RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, RTDIGESTTYPE enmDigestType, PRTMANIFESTTEST paFiles, size_t cFiles)
524{
525 /* Validate input */
526 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
527 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
528 AssertPtrReturn(paFiles, VERR_INVALID_POINTER);
529 AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER);
530
531 const char *pcszDigestType;
532 switch (enmDigestType)
533 {
534 case RTDIGESTTYPE_CRC32: pcszDigestType = "CRC32"; break;
535 case RTDIGESTTYPE_CRC64: pcszDigestType = "CRC64"; break;
536 case RTDIGESTTYPE_MD5: pcszDigestType = "MD5"; break;
537 case RTDIGESTTYPE_SHA1: pcszDigestType = "SHA1"; break;
538 case RTDIGESTTYPE_SHA256: pcszDigestType = "SHA256"; break;
539 default: return VERR_INVALID_PARAMETER;
540 }
541
542 /* Calculate the size necessary for the memory buffer. */
543 size_t cbSize = 0;
544 size_t cbMaxSize = 0;
545 for (size_t i = 0; i < cFiles; ++i)
546 {
547 size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile))
548 + strlen(paFiles[i].pszTestDigest)
549 + strlen(pcszDigestType)
550 + 6;
551 cbMaxSize = RT_MAX(cbMaxSize, cbTmp);
552 cbSize += cbTmp;
553 }
554
555 /* Create the memory buffer */
556 void *pvBuf = RTMemAlloc(cbSize);
557 if (!pvBuf)
558 return VERR_NO_MEMORY;
559
560 /* Allocate a temporary string buffer. */
561 char *pszTmp = RTStrAlloc(cbMaxSize + 1);
562 if (!pszTmp)
563 {
564 RTMemFree(pvBuf);
565 return VERR_NO_MEMORY;
566 }
567 size_t cbPos = 0;
568
569 for (size_t i = 0; i < cFiles; ++i)
570 {
571 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "%s (%s)= %s\n", pcszDigestType, RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest);
572 memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch);
573 cbPos += cch;
574 }
575 RTStrFree(pszTmp);
576
577 /* Results */
578 *ppvBuf = pvBuf;
579 *pcbSize = cbSize;
580
581 return VINF_SUCCESS;
582}
583
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