VirtualBox

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

Last change on this file since 90803 was 84509, checked in by vboxsync, 5 years ago

iprt/cdefs.h,*: Introducing RT_FLEXIBLE_ARRAY_EXTENSION as a g++ hack that allows us to use RT_FLEXIBLE_ARRAY without the compiler going all pendantic on us. Only tested with 10.1.0. bugref:9746

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