VirtualBox

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

Last change on this file since 68918 was 68918, checked in by vboxsync, 7 years ago

Main,iprt: Don't fail an import just because the manifest contains more entries than what we processed during the import.

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