VirtualBox

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

Last change on this file was 99758, checked in by vboxsync, 13 months ago

IPRT: Make doxygen 1.9.6 happy. Mostly removing duplicate docs (iprt is documented in the header files). bugref:10442

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

© 2023 Oracle
ContactPrivacy policyTerms of Use