VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/checksum/manifest2.cpp@ 62538

Last change on this file since 62538 was 62477, checked in by vboxsync, 9 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1/* $Id: manifest2.cpp 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - Manifest, the core.
4 */
5
6/*
7 * Copyright (C) 2010-2016 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/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/param.h>
40#include <iprt/md5.h>
41#include <iprt/sha.h>
42#include <iprt/string.h>
43#include <iprt/vfs.h>
44
45#include "internal/magics.h"
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Manifest attribute.
53 *
54 * Used both for entries and manifest attributes.
55 */
56typedef struct RTMANIFESTATTR
57{
58 /** The string space core (szName). */
59 RTSTRSPACECORE StrCore;
60 /** The property value. */
61 char *pszValue;
62 /** The attribute type if applicable, RTMANIFEST_ATTR_UNKNOWN if not. */
63 uint32_t fType;
64 /** Whether it was visited by the equals operation or not. */
65 bool fVisited;
66 /** The normalized property name that StrCore::pszString points at. */
67 char szName[1];
68} RTMANIFESTATTR;
69/** Pointer to a manifest attribute. */
70typedef RTMANIFESTATTR *PRTMANIFESTATTR;
71
72
73/**
74 * Manifest entry.
75 */
76typedef struct RTMANIFESTENTRY
77{
78 /** The string space core (szName). */
79 RTSTRSPACECORE StrCore;
80 /** The entry attributes (hashes, checksums, size, etc) -
81 * RTMANIFESTATTR. */
82 RTSTRSPACE Attributes;
83 /** The number of attributes. */
84 uint32_t cAttributes;
85 /** Whether it was visited by the equals operation or not. */
86 bool fVisited;
87 /** The normalized entry name that StrCore::pszString points at. */
88 char szName[1];
89} RTMANIFESTENTRY;
90/** Pointer to a manifest entry. */
91typedef RTMANIFESTENTRY *PRTMANIFESTENTRY;
92
93
94/**
95 * Manifest handle data.
96 */
97typedef struct RTMANIFESTINT
98{
99 /** Magic value (RTMANIFEST_MAGIC). */
100 uint32_t u32Magic;
101 /** The number of references to this manifest. */
102 uint32_t volatile cRefs;
103 /** String space of the entries covered by this manifest -
104 * RTMANIFESTENTRY. */
105 RTSTRSPACE Entries;
106 /** The number of entries. */
107 uint32_t cEntries;
108 /** The entry for the manifest itself. */
109 RTMANIFESTENTRY SelfEntry;
110} RTMANIFESTINT;
111
112/** The value of RTMANIFESTINT::u32Magic. */
113#define RTMANIFEST_MAGIC UINT32_C(0x99998866)
114
115/**
116 * Argument package passed to rtManifestWriteStdAttr by rtManifestWriteStdEntry
117 * and RTManifestWriteStandard.
118 */
119typedef struct RTMANIFESTWRITESTDATTR
120{
121 /** The entry name. */
122 const char *pszEntry;
123 /** The output I/O stream. */
124 RTVFSIOSTREAM hVfsIos;
125} RTMANIFESTWRITESTDATTR;
126
127
128/**
129 * Argument package used by RTManifestEqualsEx to pass its arguments to the
130 * enumeration callback functions.
131 */
132typedef struct RTMANIFESTEQUALS
133{
134 /** Name of entries to ignore. */
135 const char * const *papszIgnoreEntries;
136 /** Name of attributes to ignore. */
137 const char * const *papszIgnoreAttrs;
138 /** Flags governing the comparision. */
139 uint32_t fFlags;
140 /** Where to return an error message (++) on failure. Can be NULL. */
141 char *pszError;
142 /** The size of the buffer pszError points to. Can be 0. */
143 size_t cbError;
144
145 /** Pointer to the 2nd manifest. */
146 RTMANIFESTINT *pThis2;
147
148 /** The number of ignored entries from the 1st manifest. */
149 uint32_t cIgnoredEntries2;
150 /** The number of entries processed from the 2nd manifest. */
151 uint32_t cEntries2;
152
153 /** The number of ignored attributes from the 1st manifest. */
154 uint32_t cIgnoredAttributes1;
155 /** The number of ignored attributes from the 1st manifest. */
156 uint32_t cIgnoredAttributes2;
157 /** The number of attributes processed from the 2nd manifest. */
158 uint32_t cAttributes2;
159 /** Pointer to the string space to get matching attributes from. */
160 PRTSTRSPACE pAttributes2;
161 /** The name of the current entry.
162 * Points to an empty string it's the manifest attributes. */
163 const char *pszCurEntry;
164} RTMANIFESTEQUALS;
165/** Pointer to an RTManifestEqualEx argument packet. */
166typedef RTMANIFESTEQUALS *PRTMANIFESTEQUALS;
167
168/**
169 * Argument package used by rtMainfestQueryAttrWorker to pass its search
170 * criteria to rtMainfestQueryAttrEnumCallback and get a result back.
171 */
172typedef struct RTMANIFESTQUERYATTRARGS
173{
174 /** The attribute types we're hunting for. */
175 uint32_t fType;
176 /** What we've found. */
177 PRTMANIFESTATTR pAttr;
178} RTMANIFESTQUERYATTRARGS;
179/** Pointer to a rtMainfestQueryAttrEnumCallback argument packet. */
180typedef RTMANIFESTQUERYATTRARGS *PRTMANIFESTQUERYATTRARGS;
181
182
183/**
184 * Creates an empty manifest.
185 *
186 * @returns IPRT status code.
187 * @param fFlags Flags, MBZ.
188 * @param phManifest Where to return the handle to the manifest.
189 */
190RTDECL(int) RTManifestCreate(uint32_t fFlags, PRTMANIFEST phManifest)
191{
192 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
193 AssertPtr(phManifest);
194
195 RTMANIFESTINT *pThis = (RTMANIFESTINT *)RTMemAlloc(sizeof(*pThis));
196 if (!pThis)
197 return VERR_NO_MEMORY;
198
199 pThis->u32Magic = RTMANIFEST_MAGIC;
200 pThis->cRefs = 1;
201 pThis->Entries = NULL;
202 pThis->cEntries = 0;
203 pThis->SelfEntry.StrCore.pszString = "main";
204 pThis->SelfEntry.StrCore.cchString = 4;
205 pThis->SelfEntry.Attributes = NULL;
206 pThis->SelfEntry.cAttributes = 0;
207 pThis->SelfEntry.fVisited = false;
208 pThis->SelfEntry.szName[0] = '\0';
209
210 *phManifest = pThis;
211 return VINF_SUCCESS;
212}
213
214/**
215 * Retains a reference to the manifest handle.
216 *
217 * @returns The new reference count, UINT32_MAX if the handle is invalid.
218 * @param hManifest The handle to retain.
219 */
220RTDECL(uint32_t) RTManifestRetain(RTMANIFEST hManifest)
221{
222 RTMANIFESTINT *pThis = hManifest;
223 AssertPtrReturn(pThis, UINT32_MAX);
224 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, UINT32_MAX);
225
226 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
227 Assert(cRefs > 1 && cRefs < _1M);
228
229 return cRefs;
230}
231
232
233/**
234 * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys RTMANIFESTATTR.}
235 */
236static DECLCALLBACK(int) rtManifestDestroyAttribute(PRTSTRSPACECORE pStr, void *pvUser)
237{
238 PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
239 RTStrFree(pAttr->pszValue);
240 pAttr->pszValue = NULL;
241 RTMemFree(pAttr);
242 NOREF(pvUser);
243 return 0;
244}
245
246
247/**
248 * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys RTMANIFESTENTRY.}
249 */
250static DECLCALLBACK(int) rtManifestDestroyEntry(PRTSTRSPACECORE pStr, void *pvUser)
251{
252 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
253 RTStrSpaceDestroy(&pEntry->Attributes, rtManifestDestroyAttribute, pvUser);
254 RTMemFree(pEntry);
255 return 0;
256}
257
258
259/**
260 * Releases a reference to the manifest handle.
261 *
262 * @returns The new reference count, 0 if free. UINT32_MAX is returned if the
263 * handle is invalid.
264 * @param hManifest The handle to release.
265 * NIL is quietly ignored (returns 0).
266 */
267RTDECL(uint32_t) RTManifestRelease(RTMANIFEST hManifest)
268{
269 RTMANIFESTINT *pThis = hManifest;
270 if (pThis == NIL_RTMANIFEST)
271 return 0;
272 AssertPtrReturn(pThis, UINT32_MAX);
273 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, UINT32_MAX);
274
275 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
276 Assert(cRefs < _1M);
277 if (!cRefs)
278 {
279 ASMAtomicWriteU32(&pThis->u32Magic, ~RTMANIFEST_MAGIC);
280 RTStrSpaceDestroy(&pThis->Entries, rtManifestDestroyEntry,pThis);
281 RTStrSpaceDestroy(&pThis->SelfEntry.Attributes, rtManifestDestroyAttribute, pThis);
282 RTMemFree(pThis);
283 }
284
285 return cRefs;
286}
287
288
289/**
290 * Creates a duplicate of the specified manifest.
291 *
292 * @returns IPRT status code
293 * @param hManifestSrc The manifest to clone.
294 * @param phManifestDst Where to store the handle to the duplicate.
295 */
296RTDECL(int) RTManifestDup(RTMANIFEST hManifestSrc, PRTMANIFEST phManifestDst)
297{
298 RTMANIFESTINT *pThis = hManifestSrc;
299 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
300 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
301 AssertPtr(phManifestDst);
302
303
304 /** @todo implement cloning. */
305
306 return VERR_NOT_IMPLEMENTED;
307}
308
309
310/**
311 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation}
312 */
313static DECLCALLBACK(int) rtManifestAttributeClearVisited(PRTSTRSPACECORE pStr, void *pvUser)
314{
315 PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
316 pAttr->fVisited = false;
317 NOREF(pvUser);
318 return 0;
319}
320
321
322/**
323 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation}
324 */
325static DECLCALLBACK(int) rtManifestEntryClearVisited(PRTSTRSPACECORE pStr, void *pvUser)
326{
327 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
328 RTStrSpaceEnumerate(&pEntry->Attributes, rtManifestAttributeClearVisited, NULL);
329 pEntry->fVisited = false;
330 NOREF(pvUser);
331 return 0;
332}
333
334
335/**
336 * @callback_method_impl{FNRTSTRSPACECALLBACK, Finds the first missing}
337 */
338static DECLCALLBACK(int) rtManifestAttributeFindMissing2(PRTSTRSPACECORE pStr, void *pvUser)
339{
340 PRTMANIFESTEQUALS pEquals = (PRTMANIFESTEQUALS)pvUser;
341 PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
342
343 /*
344 * Already visited?
345 */
346 if (pAttr->fVisited)
347 return 0;
348
349 /*
350 * Ignore this entry?
351 */
352 char const * const *ppsz = pEquals->papszIgnoreAttrs;
353 if (ppsz)
354 {
355 while (*ppsz)
356 {
357 if (!strcmp(*ppsz, pAttr->szName))
358 return 0;
359 ppsz++;
360 }
361 }
362
363 /*
364 * Gotcha!
365 */
366 if (*pEquals->pszCurEntry)
367 RTStrPrintf(pEquals->pszError, pEquals->cbError,
368 "Attribute '%s' on '%s' was not found in the 1st manifest",
369 pAttr->szName, pEquals->pszCurEntry);
370 else
371 RTStrPrintf(pEquals->pszError, pEquals->cbError, "Attribute '%s' was not found in the 1st manifest", pAttr->szName);
372 return VERR_NOT_EQUAL;
373}
374
375
376/**
377 * @callback_method_impl{FNRTSTRSPACECALLBACK, Finds the first missing}
378 */
379static DECLCALLBACK(int) rtManifestEntryFindMissing2(PRTSTRSPACECORE pStr, void *pvUser)
380{
381 PRTMANIFESTEQUALS pEquals = (PRTMANIFESTEQUALS)pvUser;
382 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
383
384 /*
385 * Already visited?
386 */
387 if (pEntry->fVisited)
388 return 0;
389
390 /*
391 * Ignore this entry?
392 */
393 char const * const *ppsz = pEquals->papszIgnoreEntries;
394 if (ppsz)
395 {
396 while (*ppsz)
397 {
398 if (!strcmp(*ppsz, pEntry->StrCore.pszString))
399 return 0;
400 ppsz++;
401 }
402 }
403
404 /*
405 * Gotcha!
406 */
407 RTStrPrintf(pEquals->pszError, pEquals->cbError, "'%s' was not found in the 1st manifest", pEntry->StrCore.pszString);
408 return VERR_NOT_EQUAL;
409}
410
411
412/**
413 * @callback_method_impl{FNRTSTRSPACECALLBACK, Compares attributes}
414 */
415static DECLCALLBACK(int) rtManifestAttributeCompare(PRTSTRSPACECORE pStr, void *pvUser)
416{
417 PRTMANIFESTEQUALS pEquals = (PRTMANIFESTEQUALS)pvUser;
418 PRTMANIFESTATTR pAttr1 = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
419 PRTMANIFESTATTR pAttr2;
420
421 Assert(!pAttr1->fVisited);
422 pAttr1->fVisited = true;
423
424 /*
425 * Ignore this entry?
426 */
427 char const * const *ppsz = pEquals->papszIgnoreAttrs;
428 if (ppsz)
429 {
430 while (*ppsz)
431 {
432 if (!strcmp(*ppsz, pAttr1->szName))
433 {
434 pAttr2 = (PRTMANIFESTATTR)RTStrSpaceGet(pEquals->pAttributes2, pAttr1->szName);
435 if (pAttr2)
436 {
437 Assert(!pAttr2->fVisited);
438 pAttr2->fVisited = true;
439 pEquals->cIgnoredAttributes2++;
440 }
441 pEquals->cIgnoredAttributes1++;
442 return 0;
443 }
444 ppsz++;
445 }
446 }
447
448 /*
449 * Find the matching attribute.
450 */
451 pAttr2 = (PRTMANIFESTATTR)RTStrSpaceGet(pEquals->pAttributes2, pAttr1->szName);
452 if (!pAttr2)
453 {
454 if (pEquals->fFlags & RTMANIFEST_EQUALS_IGN_MISSING_ATTRS)
455 return 0;
456
457 if (*pEquals->pszCurEntry)
458 RTStrPrintf(pEquals->pszError, pEquals->cbError,
459 "Attribute '%s' on '%s' was not found in the 2nd manifest",
460 pAttr1->szName, pEquals->pszCurEntry);
461 else
462 RTStrPrintf(pEquals->pszError, pEquals->cbError, "Attribute '%s' was not found in the 2nd manifest", pAttr1->szName);
463 return VERR_NOT_EQUAL;
464 }
465
466 Assert(!pAttr2->fVisited);
467 pAttr2->fVisited = true;
468 pEquals->cAttributes2++;
469
470 /*
471 * Compare them.
472 */
473 if (strcmp(pAttr1->pszValue, pAttr2->pszValue))
474 {
475 if (*pEquals->pszCurEntry)
476 RTStrPrintf(pEquals->pszError, pEquals->cbError,
477 "Attribute '%s' on '%s' does not match ('%s' vs. '%s')",
478 pAttr1->szName, pEquals->pszCurEntry, pAttr1->pszValue, pAttr2->pszValue);
479 else
480 RTStrPrintf(pEquals->pszError, pEquals->cbError,
481 "Attribute '%s' does not match ('%s' vs. '%s')",
482 pAttr1->szName, pAttr1->pszValue, pAttr2->pszValue);
483 return VERR_NOT_EQUAL;
484 }
485
486 return 0;
487}
488
489
490/**
491 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation}
492 */
493DECLINLINE (int) rtManifestEntryCompare2(PRTMANIFESTEQUALS pEquals, PRTMANIFESTENTRY pEntry1, PRTMANIFESTENTRY pEntry2)
494{
495 /*
496 * Compare the attributes. It's a bit ugly with all this counting, but
497 * how else to efficiently implement RTMANIFEST_EQUALS_IGN_MISSING_ATTRS?
498 */
499 pEquals->cIgnoredAttributes1 = 0;
500 pEquals->cIgnoredAttributes2 = 0;
501 pEquals->cAttributes2 = 0;
502 pEquals->pszCurEntry = &pEntry2->szName[0];
503 pEquals->pAttributes2 = &pEntry2->Attributes;
504 int rc = RTStrSpaceEnumerate(&pEntry1->Attributes, rtManifestAttributeCompare, pEquals);
505 if (RT_SUCCESS(rc))
506 {
507 /*
508 * Check that we matched all that is required.
509 */
510 if ( pEquals->cAttributes2 + pEquals->cIgnoredAttributes2 != pEntry2->cAttributes
511 && ( !(pEquals->fFlags & RTMANIFEST_EQUALS_IGN_MISSING_ATTRS)
512 || pEquals->cIgnoredAttributes1 == pEntry1->cAttributes))
513 rc = RTStrSpaceEnumerate(&pEntry2->Attributes, rtManifestAttributeFindMissing2, pEquals);
514 }
515 return rc;
516}
517
518
519/**
520 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation}
521 */
522static DECLCALLBACK(int) rtManifestEntryCompare(PRTSTRSPACECORE pStr, void *pvUser)
523{
524 PRTMANIFESTEQUALS pEquals = (PRTMANIFESTEQUALS)pvUser;
525 PRTMANIFESTENTRY pEntry1 = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
526 PRTMANIFESTENTRY pEntry2;
527
528 /*
529 * Ignore this entry?
530 */
531 char const * const *ppsz = pEquals->papszIgnoreEntries;
532 if (ppsz)
533 {
534 while (*ppsz)
535 {
536 if (!strcmp(*ppsz, pStr->pszString))
537 {
538 pEntry2 = (PRTMANIFESTENTRY)RTStrSpaceGet(&pEquals->pThis2->Entries, pStr->pszString);
539 if (pEntry2)
540 {
541 pEntry2->fVisited = true;
542 pEquals->cIgnoredEntries2++;
543 }
544 pEntry1->fVisited = true;
545 return 0;
546 }
547 ppsz++;
548 }
549 }
550
551 /*
552 * Try find the entry in the other manifest.
553 */
554 pEntry2 = (PRTMANIFESTENTRY)RTStrSpaceGet(&pEquals->pThis2->Entries, pEntry1->StrCore.pszString);
555 if (!pEntry2)
556 {
557 RTStrPrintf(pEquals->pszError, pEquals->cbError, "'%s' not found in the 2nd manifest", pEntry1->StrCore.pszString);
558 return VERR_NOT_EQUAL;
559 }
560
561 Assert(!pEntry1->fVisited);
562 Assert(!pEntry2->fVisited);
563 pEntry1->fVisited = true;
564 pEntry2->fVisited = true;
565 pEquals->cEntries2++;
566
567 return rtManifestEntryCompare2(pEquals, pEntry1, pEntry2);
568}
569
570
571
572RTDECL(int) RTManifestEqualsEx(RTMANIFEST hManifest1, RTMANIFEST hManifest2, const char * const *papszIgnoreEntries,
573 const char * const *papszIgnoreAttrs, uint32_t fFlags, char *pszError, size_t cbError)
574{
575 /*
576 * Validate input.
577 */
578 AssertPtrNullReturn(pszError, VERR_INVALID_POINTER);
579 if (pszError && cbError)
580 *pszError = '\0';
581 RTMANIFESTINT *pThis1 = hManifest1;
582 RTMANIFESTINT *pThis2 = hManifest2;
583 if (pThis1 != NIL_RTMANIFEST)
584 {
585 AssertPtrReturn(pThis1, VERR_INVALID_HANDLE);
586 AssertReturn(pThis1->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
587 }
588 if (pThis2 != NIL_RTMANIFEST)
589 {
590 AssertPtrReturn(pThis2, VERR_INVALID_HANDLE);
591 AssertReturn(pThis2->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
592 }
593 AssertReturn(!(fFlags & ~(RTMANIFEST_EQUALS_IGN_MISSING_ATTRS)), VERR_INVALID_PARAMETER);
594
595 /*
596 * The simple cases.
597 */
598 if (pThis1 == pThis2)
599 return VINF_SUCCESS;
600 if (pThis1 == NIL_RTMANIFEST || pThis2 == NIL_RTMANIFEST)
601 return VERR_NOT_EQUAL;
602
603 /*
604 * Since we have to use callback style enumeration, we have to mark the
605 * entries and attributes to make sure we've covered them all.
606 */
607 RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryClearVisited, NULL);
608 RTStrSpaceEnumerate(&pThis2->Entries, rtManifestEntryClearVisited, NULL);
609 RTStrSpaceEnumerate(&pThis1->SelfEntry.Attributes, rtManifestAttributeClearVisited, NULL);
610 RTStrSpaceEnumerate(&pThis2->SelfEntry.Attributes, rtManifestAttributeClearVisited, NULL);
611
612 RTMANIFESTEQUALS Equals;
613 Equals.pThis2 = pThis2;
614 Equals.fFlags = fFlags;
615 Equals.papszIgnoreEntries = papszIgnoreEntries;
616 Equals.papszIgnoreAttrs = papszIgnoreAttrs;
617 Equals.pszError = pszError;
618 Equals.cbError = cbError;
619
620 Equals.cIgnoredEntries2 = 0;
621 Equals.cEntries2 = 0;
622 Equals.cIgnoredAttributes1 = 0;
623 Equals.cIgnoredAttributes2 = 0;
624 Equals.cAttributes2 = 0;
625 Equals.pAttributes2 = NULL;
626 Equals.pszCurEntry = NULL;
627
628 int rc = rtManifestEntryCompare2(&Equals, &pThis1->SelfEntry, &pThis2->SelfEntry);
629 if (RT_SUCCESS(rc))
630 rc = RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryCompare, &Equals);
631 if (RT_SUCCESS(rc))
632 {
633 /*
634 * Did we cover all entries of the 2nd manifest?
635 */
636 if (Equals.cEntries2 + Equals.cIgnoredEntries2 != pThis2->cEntries)
637 rc = RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryFindMissing2, &Equals);
638 }
639
640 return rc;
641}
642
643
644RTDECL(int) RTManifestEquals(RTMANIFEST hManifest1, RTMANIFEST hManifest2)
645{
646 return RTManifestEqualsEx(hManifest1, hManifest2,
647 NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttrs*/,
648 0 /*fFlags*/, NULL, 0);
649}
650
651
652/**
653 * Translates a attribyte type to a attribute name.
654 *
655 * @returns Attribute name for fFlags, NULL if not translatable.
656 * @param fType The type flags. Only one bit should be set.
657 */
658static const char *rtManifestTypeToAttrName(uint32_t fType)
659{
660 switch (fType)
661 {
662 case RTMANIFEST_ATTR_SIZE: return "SIZE";
663 case RTMANIFEST_ATTR_MD5: return "MD5";
664 case RTMANIFEST_ATTR_SHA1: return "SHA1";
665 case RTMANIFEST_ATTR_SHA256: return "SHA256";
666 case RTMANIFEST_ATTR_SHA512: return "SHA512";
667 default: return NULL;
668 }
669}
670
671
672/**
673 * Worker common to RTManifestSetAttr and RTManifestEntrySetAttr.
674 *
675 * @returns IPRT status code.
676 * @param pEntry Pointer to the entry.
677 * @param pszAttr The name of the attribute to add.
678 * @param pszValue The value string.
679 * @param fType The attribute type type.
680 */
681static int rtManifestSetAttrWorker(PRTMANIFESTENTRY pEntry, const char *pszAttr, const char *pszValue, uint32_t fType)
682{
683 char *pszValueCopy;
684 int rc = RTStrDupEx(&pszValueCopy, pszValue);
685 if (RT_FAILURE(rc))
686 return rc;
687
688 /*
689 * Does the attribute exist already?
690 */
691 AssertCompileMemberOffset(RTMANIFESTATTR, StrCore, 0);
692 PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)RTStrSpaceGet(&pEntry->Attributes, pszAttr);
693 if (pAttr)
694 {
695 RTStrFree(pAttr->pszValue);
696 pAttr->pszValue = pszValueCopy;
697 pAttr->fType = fType;
698 }
699 else
700 {
701 size_t cbName = strlen(pszAttr) + 1;
702 pAttr = (PRTMANIFESTATTR)RTMemAllocVar(RT_OFFSETOF(RTMANIFESTATTR, szName[cbName]));
703 if (!pAttr)
704 {
705 RTStrFree(pszValueCopy);
706 return VERR_NO_MEMORY;
707 }
708 memcpy(pAttr->szName, pszAttr, cbName);
709 pAttr->StrCore.pszString = pAttr->szName;
710 pAttr->StrCore.cchString = cbName - 1;
711 pAttr->pszValue = pszValueCopy;
712 pAttr->fType = fType;
713 if (RT_UNLIKELY(!RTStrSpaceInsert(&pEntry->Attributes, &pAttr->StrCore)))
714 {
715 AssertFailed();
716 RTStrFree(pszValueCopy);
717 RTMemFree(pAttr);
718 return VERR_INTERNAL_ERROR_4;
719 }
720 pEntry->cAttributes++;
721 }
722
723 return VINF_SUCCESS;
724}
725
726
727/**
728 * Sets a manifest attribute.
729 *
730 * @returns IPRT status code.
731 * @param hManifest The manifest handle.
732 * @param pszAttr The attribute name. If this already exists,
733 * its value will be replaced.
734 * @param pszValue The value string.
735 * @param fType The attribute type, pass
736 * RTMANIFEST_ATTR_UNKNOWN if not known.
737 */
738RTDECL(int) RTManifestSetAttr(RTMANIFEST hManifest, const char *pszAttr, const char *pszValue, uint32_t fType)
739{
740 RTMANIFESTINT *pThis = hManifest;
741 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
742 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
743 AssertPtr(pszValue);
744 AssertReturn(RT_IS_POWER_OF_TWO(fType) && fType < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER);
745 if (!pszAttr)
746 pszAttr = rtManifestTypeToAttrName(fType);
747 AssertPtr(pszAttr);
748
749 return rtManifestSetAttrWorker(&pThis->SelfEntry, pszAttr, pszValue, fType);
750}
751
752
753/**
754 * Worker common to RTManifestUnsetAttr and RTManifestEntryUnsetAttr.
755 *
756 * @returns IPRT status code.
757 * @param pEntry Pointer to the entry.
758 * @param pszAttr The name of the attribute to remove.
759 */
760static int rtManifestUnsetAttrWorker(PRTMANIFESTENTRY pEntry, const char *pszAttr)
761{
762 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&pEntry->Attributes, pszAttr);
763 if (!pStrCore)
764 return VWRN_NOT_FOUND;
765 pEntry->cAttributes--;
766 rtManifestDestroyAttribute(pStrCore, NULL);
767 return VINF_SUCCESS;
768}
769
770
771/**
772 * Unsets (removes) a manifest attribute if it exists.
773 *
774 * @returns IPRT status code.
775 * @retval VWRN_NOT_FOUND if not found.
776 *
777 * @param hManifest The manifest handle.
778 * @param pszAttr The attribute name.
779 */
780RTDECL(int) RTManifestUnsetAttr(RTMANIFEST hManifest, const char *pszAttr)
781{
782 RTMANIFESTINT *pThis = hManifest;
783 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
784 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
785 AssertPtr(pszAttr);
786
787 return rtManifestUnsetAttrWorker(&pThis->SelfEntry, pszAttr);
788}
789
790
791/**
792 * Callback employed by rtManifestQueryAttrWorker to search by attribute type.
793 *
794 * @returns VINF_SUCCESS or VINF_CALLBACK_RETURN.
795 * @param pStr The attribute string node.
796 * @param pvUser The argument package.
797 */
798static DECLCALLBACK(int) rtMainfestQueryAttrEnumCallback(PRTSTRSPACECORE pStr, void *pvUser)
799{
800 PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)pStr;
801 PRTMANIFESTQUERYATTRARGS pArgs = (PRTMANIFESTQUERYATTRARGS)pvUser;
802
803 if (pAttr->fType & pArgs->fType)
804 {
805 pArgs->pAttr = pAttr;
806 return VINF_CALLBACK_RETURN;
807 }
808 return VINF_SUCCESS;
809}
810
811
812/**
813 * Worker common to RTManifestQueryAttr and RTManifestEntryQueryAttr.
814 *
815 * @returns IPRT status code.
816 * @param pEntry The entry.
817 * @param pszAttr The attribute name. If NULL, it will be
818 * selected by @a fType alone.
819 * @param fType The attribute types the entry should match. Pass
820 * Pass RTMANIFEST_ATTR_ANY match any. If more
821 * than one is given, the first matching one is
822 * returned.
823 * @param pszValue Where to return value.
824 * @param cbValue The size of the buffer @a pszValue points to.
825 * @param pfType Where to return the attribute type value.
826 */
827static int rtManifestQueryAttrWorker(PRTMANIFESTENTRY pEntry, const char *pszAttr, uint32_t fType,
828 char *pszValue, size_t cbValue, uint32_t *pfType)
829{
830 /*
831 * Find the requested attribute.
832 */
833 PRTMANIFESTATTR pAttr;
834 if (pszAttr)
835 {
836 /* By name. */
837 pAttr = (PRTMANIFESTATTR)RTStrSpaceGet(&pEntry->Attributes, pszAttr);
838 if (!pAttr)
839 return VERR_MANIFEST_ATTR_NOT_FOUND;
840 if (!(pAttr->fType & fType))
841 return VERR_MANIFEST_ATTR_TYPE_MISMATCH;
842 }
843 else
844 {
845 /* By type. */
846 RTMANIFESTQUERYATTRARGS Args;
847 Args.fType = fType;
848 Args.pAttr = NULL;
849 int rc = RTStrSpaceEnumerate(&pEntry->Attributes, rtMainfestQueryAttrEnumCallback, &Args);
850 AssertRCReturn(rc, rc);
851 pAttr = Args.pAttr;
852 if (!pAttr)
853 return VERR_MANIFEST_ATTR_TYPE_NOT_FOUND;
854 }
855
856 /*
857 * Set the return values.
858 */
859 if (cbValue || pszValue)
860 {
861 size_t cbNeeded = strlen(pAttr->pszValue) + 1;
862 if (cbNeeded > cbValue)
863 return VERR_BUFFER_OVERFLOW;
864 memcpy(pszValue, pAttr->pszValue, cbNeeded);
865 }
866
867 if (pfType)
868 *pfType = pAttr->fType;
869
870 return VINF_SUCCESS;
871}
872
873
874RTDECL(int) RTManifestQueryAttr(RTMANIFEST hManifest, const char *pszAttr, uint32_t fType,
875 char *pszValue, size_t cbValue, uint32_t *pfType)
876{
877 RTMANIFESTINT *pThis = hManifest;
878 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
879 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
880 AssertPtrNull(pszAttr);
881 AssertPtr(pszValue);
882
883 return rtManifestQueryAttrWorker(&pThis->SelfEntry, pszAttr, fType, pszValue, cbValue, pfType);
884}
885
886
887/**
888 * Callback employed by RTManifestQueryAllAttrTypes to collect attribute types.
889 *
890 * @returns VINF_SUCCESS.
891 * @param pStr The attribute string node.
892 * @param pvUser Pointer to type flags (uint32_t).
893 */
894static DECLCALLBACK(int) rtMainfestQueryAllAttrTypesEnumAttrCallback(PRTSTRSPACECORE pStr, void *pvUser)
895{
896 PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)pStr;
897 uint32_t *pfTypes = (uint32_t *)pvUser;
898 *pfTypes |= pAttr->fType;
899 return VINF_SUCCESS;
900}
901
902
903/**
904 * Callback employed by RTManifestQueryAllAttrTypes to collect attribute types
905 * for an entry.
906 *
907 * @returns VINF_SUCCESS.
908 * @param pStr The attribute string node.
909 * @param pvUser Pointer to type flags (uint32_t).
910 */
911static DECLCALLBACK(int) rtMainfestQueryAllAttrTypesEnumEntryCallback(PRTSTRSPACECORE pStr, void *pvUser)
912{
913 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
914 return RTStrSpaceEnumerate(&pEntry->Attributes, rtMainfestQueryAllAttrTypesEnumAttrCallback, pvUser);
915}
916
917
918RTDECL(int) RTManifestQueryAllAttrTypes(RTMANIFEST hManifest, bool fEntriesOnly, uint32_t *pfTypes)
919{
920 RTMANIFESTINT *pThis = hManifest;
921 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
922 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
923 AssertPtr(pfTypes);
924
925 *pfTypes = 0;
926 int rc = RTStrSpaceEnumerate(&pThis->Entries, rtMainfestQueryAllAttrTypesEnumEntryCallback, pfTypes);
927 if (RT_SUCCESS(rc) && fEntriesOnly)
928 rc = rtMainfestQueryAllAttrTypesEnumAttrCallback(&pThis->SelfEntry.StrCore, pfTypes);
929 return VINF_SUCCESS;
930}
931
932
933/**
934 * Validates the name entry.
935 *
936 * @returns IPRT status code.
937 * @param pszEntry The entry name to validate.
938 * @param pfNeedNormalization Where to return whether it needs normalization
939 * or not. Optional.
940 * @param pcchEntry Where to return the length. Optional.
941 */
942static int rtManifestValidateNameEntry(const char *pszEntry, bool *pfNeedNormalization, size_t *pcchEntry)
943{
944 int rc;
945 bool fNeedNormalization = false;
946 const char *pszCur = pszEntry;
947
948 for (;;)
949 {
950 RTUNICP uc;
951 rc = RTStrGetCpEx(&pszCur, &uc);
952 if (RT_FAILURE(rc))
953 return rc;
954 if (!uc)
955 break;
956 if (uc == '\\')
957 fNeedNormalization = true;
958 else if (uc < 32 || uc == ':' || uc == '(' || uc == ')')
959 return VERR_INVALID_NAME;
960 }
961
962 if (pfNeedNormalization)
963 *pfNeedNormalization = fNeedNormalization;
964
965 size_t cchEntry = pszCur - pszEntry - 1;
966 if (!cchEntry)
967 rc = VERR_INVALID_NAME;
968 if (pcchEntry)
969 *pcchEntry = cchEntry;
970
971 return rc;
972}
973
974
975/**
976 * Normalizes a entry name.
977 *
978 * @param pszEntry The entry name to normalize.
979 */
980static void rtManifestNormalizeEntry(char *pszEntry)
981{
982 char ch;
983 while ((ch = *pszEntry))
984 {
985 if (ch == '\\')
986 *pszEntry = '/';
987 pszEntry++;
988 }
989}
990
991
992/**
993 * Gets an entry.
994 *
995 * @returns IPRT status code.
996 * @param pThis The manifest to work with.
997 * @param pszEntry The entry name.
998 * @param fNeedNormalization Whether rtManifestValidateNameEntry said it
999 * needed normalization.
1000 * @param cchEntry The length of the name.
1001 * @param ppEntry Where to return the entry pointer on success.
1002 */
1003static int rtManifestGetEntry(RTMANIFESTINT *pThis, const char *pszEntry, bool fNeedNormalization, size_t cchEntry,
1004 PRTMANIFESTENTRY *ppEntry)
1005{
1006 PRTMANIFESTENTRY pEntry;
1007
1008 AssertCompileMemberOffset(RTMANIFESTATTR, StrCore, 0);
1009 if (!fNeedNormalization)
1010 pEntry = (PRTMANIFESTENTRY)RTStrSpaceGet(&pThis->Entries, pszEntry);
1011 else
1012 {
1013 char *pszCopy = (char *)RTMemTmpAlloc(cchEntry + 1);
1014 if (RT_UNLIKELY(!pszCopy))
1015 return VERR_NO_TMP_MEMORY;
1016 memcpy(pszCopy, pszEntry, cchEntry + 1);
1017 rtManifestNormalizeEntry(pszCopy);
1018
1019 pEntry = (PRTMANIFESTENTRY)RTStrSpaceGet(&pThis->Entries, pszCopy);
1020 RTMemTmpFree(pszCopy);
1021 }
1022
1023 *ppEntry = pEntry;
1024 return pEntry ? VINF_SUCCESS : VERR_NOT_FOUND;
1025}
1026
1027
1028/**
1029 * Sets an attribute of a manifest entry.
1030 *
1031 * @returns IPRT status code.
1032 * @param hManifest The manifest handle.
1033 * @param pszEntry The entry name. This will automatically be
1034 * added if there was no previous call to
1035 * RTManifestEntryAdd for this name. See
1036 * RTManifestEntryAdd for the entry name rules.
1037 * @param pszAttr The attribute name. If this already exists,
1038 * its value will be replaced.
1039 * @param pszValue The value string.
1040 * @param fType The attribute type, pass
1041 * RTMANIFEST_ATTR_UNKNOWN if not known.
1042 */
1043RTDECL(int) RTManifestEntrySetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr,
1044 const char *pszValue, uint32_t fType)
1045{
1046 RTMANIFESTINT *pThis = hManifest;
1047 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1048 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1049 AssertPtr(pszEntry);
1050 AssertPtr(pszValue);
1051 AssertReturn(RT_IS_POWER_OF_TWO(fType) && fType < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER);
1052 if (!pszAttr)
1053 pszAttr = rtManifestTypeToAttrName(fType);
1054 AssertPtr(pszAttr);
1055
1056 bool fNeedNormalization;
1057 size_t cchEntry;
1058 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1059 AssertRCReturn(rc, rc);
1060
1061 /*
1062 * Resolve the entry, adding one if necessary.
1063 */
1064 PRTMANIFESTENTRY pEntry;
1065 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1066 if (rc == VERR_NOT_FOUND)
1067 {
1068 pEntry = (PRTMANIFESTENTRY)RTMemAlloc(RT_OFFSETOF(RTMANIFESTENTRY, szName[cchEntry + 1]));
1069 if (!pEntry)
1070 return VERR_NO_MEMORY;
1071
1072 pEntry->StrCore.cchString = cchEntry;
1073 pEntry->StrCore.pszString = pEntry->szName;
1074 pEntry->Attributes = NULL;
1075 pEntry->cAttributes = 0;
1076 memcpy(pEntry->szName, pszEntry, cchEntry + 1);
1077 if (fNeedNormalization)
1078 rtManifestNormalizeEntry(pEntry->szName);
1079
1080 if (!RTStrSpaceInsert(&pThis->Entries, &pEntry->StrCore))
1081 {
1082 RTMemFree(pEntry);
1083 return VERR_INTERNAL_ERROR_4;
1084 }
1085 pThis->cEntries++;
1086 }
1087 else if (RT_FAILURE(rc))
1088 return rc;
1089
1090 return rtManifestSetAttrWorker(pEntry, pszAttr, pszValue, fType);
1091}
1092
1093
1094/**
1095 * Unsets (removes) an attribute of a manifest entry if they both exist.
1096 *
1097 * @returns IPRT status code.
1098 * @retval VWRN_NOT_FOUND if not found.
1099 *
1100 * @param hManifest The manifest handle.
1101 * @param pszEntry The entry name.
1102 * @param pszAttr The attribute name.
1103 */
1104RTDECL(int) RTManifestEntryUnsetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr)
1105{
1106 RTMANIFESTINT *pThis = hManifest;
1107 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1108 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1109 AssertPtr(pszEntry);
1110 AssertPtr(pszAttr);
1111
1112 bool fNeedNormalization;
1113 size_t cchEntry;
1114 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1115 AssertRCReturn(rc, rc);
1116
1117 /*
1118 * Resolve the entry and hand it over to the worker.
1119 */
1120 PRTMANIFESTENTRY pEntry;
1121 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1122 if (RT_SUCCESS(rc))
1123 rc = rtManifestUnsetAttrWorker(pEntry, pszAttr);
1124 return rc;
1125}
1126
1127
1128RTDECL(int) RTManifestEntryQueryAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr, uint32_t fType,
1129 char *pszValue, size_t cbValue, uint32_t *pfType)
1130{
1131 RTMANIFESTINT *pThis = hManifest;
1132 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1133 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1134 AssertPtr(pszEntry);
1135 AssertPtrNull(pszAttr);
1136 AssertPtr(pszValue);
1137
1138 /*
1139 * Look up the entry.
1140 */
1141 bool fNeedNormalization;
1142 size_t cchEntry;
1143 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1144 AssertRCReturn(rc, rc);
1145
1146 PRTMANIFESTENTRY pEntry;
1147 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1148 if (RT_SUCCESS(rc))
1149 rc = rtManifestQueryAttrWorker(pEntry, pszAttr, fType, pszValue, cbValue, pfType);
1150 return rc;
1151}
1152
1153
1154/**
1155 * Adds a new entry to a manifest.
1156 *
1157 * The entry name rules:
1158 * - The entry name can contain any character defined by unicode, except
1159 * control characters, ':', '(' and ')'. The exceptions are mainly there
1160 * because of uncertainty around how various formats handles these.
1161 * - It is considered case sensitive.
1162 * - Forward (unix) and backward (dos) slashes are considered path
1163 * separators and converted to forward slashes.
1164 *
1165 * @returns IPRT status code.
1166 * @retval VWRN_ALREADY_EXISTS if the entry already exists.
1167 *
1168 * @param hManifest The manifest handle.
1169 * @param pszEntry The entry name (UTF-8).
1170 *
1171 * @remarks Some manifest formats will not be able to store an entry without
1172 * any attributes. So, this is just here in case it comes in handy
1173 * when dealing with formats which can.
1174 */
1175RTDECL(int) RTManifestEntryAdd(RTMANIFEST hManifest, const char *pszEntry)
1176{
1177 RTMANIFESTINT *pThis = hManifest;
1178 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1179 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1180 AssertPtr(pszEntry);
1181
1182 bool fNeedNormalization;
1183 size_t cchEntry;
1184 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1185 AssertRCReturn(rc, rc);
1186
1187 /*
1188 * Only add one if it does not already exist.
1189 */
1190 PRTMANIFESTENTRY pEntry;
1191 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1192 if (rc == VERR_NOT_FOUND)
1193 {
1194 pEntry = (PRTMANIFESTENTRY)RTMemAlloc(RT_OFFSETOF(RTMANIFESTENTRY, szName[cchEntry + 1]));
1195 if (pEntry)
1196 {
1197 pEntry->StrCore.cchString = cchEntry;
1198 pEntry->StrCore.pszString = pEntry->szName;
1199 pEntry->Attributes = NULL;
1200 pEntry->cAttributes = 0;
1201 memcpy(pEntry->szName, pszEntry, cchEntry + 1);
1202 if (fNeedNormalization)
1203 rtManifestNormalizeEntry(pEntry->szName);
1204
1205 if (RTStrSpaceInsert(&pThis->Entries, &pEntry->StrCore))
1206 {
1207 pThis->cEntries++;
1208 rc = VINF_SUCCESS;
1209 }
1210 else
1211 {
1212 RTMemFree(pEntry);
1213 rc = VERR_INTERNAL_ERROR_4;
1214 }
1215 }
1216 else
1217 rc = VERR_NO_MEMORY;
1218 }
1219 else if (RT_SUCCESS(rc))
1220 rc = VWRN_ALREADY_EXISTS;
1221
1222 return rc;
1223}
1224
1225
1226/**
1227 * Removes an entry.
1228 *
1229 * @returns IPRT status code.
1230 * @param hManifest The manifest handle.
1231 * @param pszEntry The entry name.
1232 */
1233RTDECL(int) RTManifestEntryRemove(RTMANIFEST hManifest, const char *pszEntry)
1234{
1235 RTMANIFESTINT *pThis = hManifest;
1236 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1237 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1238 AssertPtr(pszEntry);
1239
1240 bool fNeedNormalization;
1241 size_t cchEntry;
1242 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1243 AssertRCReturn(rc, rc);
1244
1245 /*
1246 * Look it up before removing it.
1247 */
1248 PRTMANIFESTENTRY pEntry;
1249 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1250 if (RT_SUCCESS(rc))
1251 {
1252 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&pThis->Entries, pEntry->StrCore.pszString);
1253 AssertReturn(pStrCore, VERR_INTERNAL_ERROR_3);
1254 pThis->cEntries--;
1255 rtManifestDestroyEntry(pStrCore, pThis);
1256 }
1257
1258 return rc;
1259}
1260
1261
1262RTDECL(bool) RTManifestEntryExists(RTMANIFEST hManifest, const char *pszEntry)
1263{
1264 RTMANIFESTINT *pThis = hManifest;
1265 AssertPtrReturn(pThis, false);
1266 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, false);
1267 AssertPtr(pszEntry);
1268
1269 bool fNeedNormalization;
1270 size_t cchEntry;
1271 int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
1272 AssertRCReturn(rc, false);
1273
1274 /*
1275 * Check if it exists.
1276 */
1277 PRTMANIFESTENTRY pEntry;
1278 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
1279 return RT_SUCCESS_NP(rc);
1280}
1281
1282
1283/**
1284 * Reads a line from a VFS I/O stream.
1285 *
1286 * @todo Replace this with a buffered I/O stream layer.
1287 *
1288 * @returns IPRT status code. VERR_EOF when trying to read beyond the stream
1289 * end.
1290 * @param hVfsIos The I/O stream to read from.
1291 * @param pszLine Where to store what we've read.
1292 * @param cbLine The number of bytes to read.
1293 */
1294static int rtManifestReadLine(RTVFSIOSTREAM hVfsIos, char *pszLine, size_t cbLine)
1295{
1296 /* This is horribly slow right now, but it's not a biggy as the input is
1297 usually cached in memory somewhere... */
1298 *pszLine = '\0';
1299 while (cbLine > 1)
1300 {
1301 char ch;
1302 int rc = RTVfsIoStrmRead(hVfsIos, &ch, 1, true /*fBLocking*/, NULL);
1303 if (RT_FAILURE(rc))
1304 return rc;
1305
1306 /* \r\n */
1307 if (ch == '\r')
1308 {
1309 if (cbLine <= 2)
1310 {
1311 pszLine[0] = ch;
1312 pszLine[1] = '\0';
1313 return VINF_BUFFER_OVERFLOW;
1314 }
1315
1316 rc = RTVfsIoStrmRead(hVfsIos, &ch, 1, true /*fBLocking*/, NULL);
1317 if (RT_SUCCESS(rc) && ch == '\n')
1318 return VINF_SUCCESS;
1319 pszLine[0] = '\r';
1320 pszLine[1] = ch;
1321 pszLine[2] = '\0';
1322 if (RT_FAILURE(rc))
1323 return rc == VERR_EOF ? VINF_EOF : rc;
1324 }
1325
1326 /* \n */
1327 if (ch == '\n')
1328 return VINF_SUCCESS;
1329
1330 /* add character. */
1331 pszLine[0] = ch;
1332 pszLine[1] = '\0';
1333
1334 /* advance */
1335 pszLine++;
1336 cbLine--;
1337 }
1338
1339 return VINF_BUFFER_OVERFLOW;
1340}
1341
1342
1343RTDECL(int) RTManifestReadStandardEx(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos, char *pszErr, size_t cbErr)
1344{
1345 /*
1346 * Validate input.
1347 */
1348 AssertPtrNull(pszErr);
1349 if (pszErr && cbErr)
1350 *pszErr = '\0';
1351 RTMANIFESTINT *pThis = hManifest;
1352 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1353 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1354
1355 /*
1356 * Process the stream line by line.
1357 */
1358 uint32_t iLine = 0;
1359 for (;;)
1360 {
1361 /*
1362 * Read a line from the input stream.
1363 */
1364 iLine++;
1365 char szLine[RTPATH_MAX + RTSHA512_DIGEST_LEN + 32];
1366 int rc = rtManifestReadLine(hVfsIos, szLine, sizeof(szLine));
1367 if (RT_FAILURE(rc))
1368 {
1369 if (rc == VERR_EOF)
1370 return VINF_SUCCESS;
1371 RTStrPrintf(pszErr, cbErr, "Error reading line #%u: %Rrc", iLine, rc);
1372 return rc;
1373 }
1374 if (rc != VINF_SUCCESS)
1375 {
1376 RTStrPrintf(pszErr, cbErr, "Line number %u is too long", iLine);
1377 return VERR_OUT_OF_RANGE;
1378 }
1379
1380 /*
1381 * Strip it and skip if empty.
1382 */
1383 char *psz = RTStrStrip(szLine);
1384 if (!*psz)
1385 continue;
1386
1387 /*
1388 * Read the attribute name.
1389 */
1390 char ch;
1391 const char * const pszAttr = psz;
1392 do
1393 psz++;
1394 while (!RT_C_IS_BLANK((ch = *psz)) && ch && ch != '(');
1395 if (ch)
1396 *psz++ = '\0';
1397
1398 /*
1399 * The entry name is enclosed in parenthesis and followed by a '='.
1400 */
1401 if (ch != '(')
1402 {
1403 psz = RTStrStripL(psz);
1404 ch = *psz++;
1405 if (ch != '(')
1406 {
1407 RTStrPrintf(pszErr, cbErr, "Expected '(' after %zu on line %u", psz - szLine - 1, iLine);
1408 return VERR_PARSE_ERROR;
1409 }
1410 }
1411 const char * const pszName = psz;
1412 while ((ch = *psz) != '\0')
1413 {
1414 if (ch == ')')
1415 {
1416 char *psz2 = RTStrStripL(psz + 1);
1417 if (*psz2 == '=')
1418 {
1419 *psz = '\0';
1420 psz = psz2;
1421 break;
1422 }
1423 }
1424 psz++;
1425 }
1426
1427 if (*psz != '=')
1428 {
1429 RTStrPrintf(pszErr, cbErr, "Expected ')=' at %zu on line %u", psz - szLine, iLine);
1430 return VERR_PARSE_ERROR;
1431 }
1432
1433 /*
1434 * The value.
1435 */
1436 psz = RTStrStrip(psz + 1);
1437 const char * const pszValue = psz;
1438 if (!*psz)
1439 {
1440 RTStrPrintf(pszErr, cbErr, "Expected value at %zu on line %u", psz - szLine, iLine);
1441 return VERR_PARSE_ERROR;
1442 }
1443
1444 /*
1445 * Detect attribute type and sanity check the value.
1446 */
1447 uint32_t fType = RTMANIFEST_ATTR_UNKNOWN;
1448 static const struct
1449 {
1450 const char *pszAttr;
1451 uint32_t fType;
1452 unsigned cBits;
1453 unsigned uBase;
1454 } s_aDecAttrs[] =
1455 {
1456 { "SIZE", RTMANIFEST_ATTR_SIZE, 64, 10}
1457 };
1458 for (unsigned i = 0; i < RT_ELEMENTS(s_aDecAttrs); i++)
1459 if (!strcmp(s_aDecAttrs[i].pszAttr, pszAttr))
1460 {
1461 fType = s_aDecAttrs[i].fType;
1462 rc = RTStrToUInt64Full(pszValue, s_aDecAttrs[i].uBase, NULL);
1463 if (rc != VINF_SUCCESS)
1464 {
1465 RTStrPrintf(pszErr, cbErr, "Malformed value ('%s') at %zu on line %u: %Rrc", pszValue, psz - szLine, iLine, rc);
1466 return VERR_PARSE_ERROR;
1467 }
1468 break;
1469 }
1470
1471 if (fType == RTMANIFEST_ATTR_UNKNOWN)
1472 {
1473 static const struct
1474 {
1475 const char *pszAttr;
1476 uint32_t fType;
1477 unsigned cchHex;
1478 } s_aHexAttrs[] =
1479 {
1480 { "MD5", RTMANIFEST_ATTR_MD5, RTMD5_DIGEST_LEN },
1481 { "SHA1", RTMANIFEST_ATTR_SHA1, RTSHA1_DIGEST_LEN },
1482 { "SHA256", RTMANIFEST_ATTR_SHA256, RTSHA256_DIGEST_LEN },
1483 { "SHA512", RTMANIFEST_ATTR_SHA512, RTSHA512_DIGEST_LEN }
1484 };
1485 for (unsigned i = 0; i < RT_ELEMENTS(s_aHexAttrs); i++)
1486 if (!strcmp(s_aHexAttrs[i].pszAttr, pszAttr))
1487 {
1488 fType = s_aHexAttrs[i].fType;
1489 for (unsigned off = 0; off < s_aHexAttrs[i].cchHex; off++)
1490 if (!RT_C_IS_XDIGIT(pszValue[off]))
1491 {
1492 RTStrPrintf(pszErr, cbErr, "Expected hex digit at %zu on line %u (value '%s', pos %u)",
1493 pszValue - szLine + off, iLine, pszValue, off);
1494 return VERR_PARSE_ERROR;
1495 }
1496 break;
1497 }
1498 }
1499
1500 /*
1501 * Finally, add it.
1502 */
1503 rc = RTManifestEntrySetAttr(hManifest, pszName, pszAttr, pszValue, fType);
1504 if (RT_FAILURE(rc))
1505 {
1506 RTStrPrintf(pszErr, cbErr, "RTManifestEntrySetAttr(,'%s','%s', '%s', %#x) failed on line %u: %Rrc",
1507 pszName, pszAttr, pszValue, fType, iLine, rc);
1508 return rc;
1509 }
1510 }
1511}
1512
1513RTDECL(int) RTManifestReadStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos)
1514{
1515 return RTManifestReadStandardEx(hManifest, hVfsIos, NULL, 0);
1516}
1517
1518
1519/**
1520 * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes RTMANIFESTATTR.}
1521 */
1522static DECLCALLBACK(int) rtManifestWriteStdAttr(PRTSTRSPACECORE pStr, void *pvUser)
1523{
1524 PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
1525 RTMANIFESTWRITESTDATTR *pArgs = (RTMANIFESTWRITESTDATTR *)pvUser;
1526 char szLine[RTPATH_MAX + RTSHA512_DIGEST_LEN + 32];
1527 size_t cchLine = RTStrPrintf(szLine, sizeof(szLine), "%s (%s) = %s\n", pAttr->szName, pArgs->pszEntry, pAttr->pszValue);
1528 if (cchLine >= sizeof(szLine) - 1)
1529 return VERR_BUFFER_OVERFLOW;
1530 return RTVfsIoStrmWrite(pArgs->hVfsIos, szLine, cchLine, true /*fBlocking*/, NULL);
1531}
1532
1533
1534/**
1535 * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes RTMANIFESTENTRY.}
1536 */
1537static DECLCALLBACK(int) rtManifestWriteStdEntry(PRTSTRSPACECORE pStr, void *pvUser)
1538{
1539 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
1540
1541 RTMANIFESTWRITESTDATTR Args;
1542 Args.hVfsIos = (RTVFSIOSTREAM)pvUser;
1543 Args.pszEntry = pStr->pszString;
1544 return RTStrSpaceEnumerate(&pEntry->Attributes, rtManifestWriteStdAttr, &Args);
1545}
1546
1547
1548RTDECL(int) RTManifestWriteStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos)
1549{
1550 RTMANIFESTINT *pThis = hManifest;
1551 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1552 AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
1553
1554 RTMANIFESTWRITESTDATTR Args;
1555 Args.hVfsIos = hVfsIos;
1556 Args.pszEntry = "main";
1557 int rc = RTStrSpaceEnumerate(&pThis->SelfEntry.Attributes, rtManifestWriteStdAttr, &Args);
1558 if (RT_SUCCESS(rc))
1559 rc = RTStrSpaceEnumerate(&pThis->Entries, rtManifestWriteStdEntry, hVfsIos);
1560 return rc;
1561}
1562
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