Changeset 34535 in vbox for trunk/src/VBox/Runtime/common/checksum
- Timestamp:
- Nov 30, 2010 5:46:14 PM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/checksum/manifest2.cpp
r34464 r34535 62 62 /** The attribute type if applicable, RTMANIFEST_ATTR_UNKNOWN if not. */ 63 63 uint32_t fType; 64 /** Whether it was visited by the equals operation or not. */ 65 bool fVisited; 64 66 /** The normalized property name that StrCore::pszString points at. */ 65 67 char szName[1]; … … 79 81 * RTMANIFESTATTR. */ 80 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; 81 87 /** The normalized entry name that StrCore::pszString points at. */ 82 88 char szName[1]; … … 95 101 /** The number of references to this manifest. */ 96 102 uint32_t volatile cRefs; 97 /** Manifest attributes - RTMANIFESTATTR. */98 RTSTRSPACE Attributes;99 103 /** String space of the entries covered by this manifest - 100 104 * RTMANIFESTENTRY. */ 101 105 RTSTRSPACE Entries; 106 /** The number of entries. */ 107 uint32_t cEntries; 108 /** The entry for the manifest itself. */ 109 RTMANIFESTENTRY SelfEntry; 102 110 } RTMANIFESTINT; 103 111 … … 119 127 120 128 /** 129 * Argument package used by RTManifestEqualsEx to pass it's arguments to the 130 * enumeration callback functions. 131 */ 132 typedef struct RTMANIFESTEQUALS 133 { 134 /** Name of entries to ignore. */ 135 const char * const *papszIgnoreEntries; 136 /** Name of attributes to ignore. */ 137 const char * const *papszIgnoreAttr; 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. */ 166 typedef RTMANIFESTEQUALS *PRTMANIFESTEQUALS; 167 168 169 /** 121 170 * Creates an empty manifest. 122 171 * … … 136 185 pThis->u32Magic = RTMANIFEST_MAGIC; 137 186 pThis->cRefs = 1; 138 pThis->Attributes = NULL;139 187 pThis->Entries = NULL; 188 pThis->cEntries = 0; 189 pThis->SelfEntry.StrCore.pszString = "main"; 190 pThis->SelfEntry.StrCore.cchString = 4; 191 pThis->SelfEntry.Attributes = 0; 192 pThis->SelfEntry.cAttributes = NULL; 193 pThis->SelfEntry.fVisited = false; 194 pThis->SelfEntry.szName[0] = '\0'; 140 195 141 196 *phManifest = pThis; … … 208 263 { 209 264 ASMAtomicWriteU32(&pThis->u32Magic, ~RTMANIFEST_MAGIC); 210 RTStrSpaceDestroy(&pThis-> Attributes, rtManifestDestroyAttribute,pThis);211 RTStrSpaceDestroy(&pThis-> Entries, rtManifestDestroyEntry,pThis);265 RTStrSpaceDestroy(&pThis->Entries, rtManifestDestroyEntry,pThis); 266 RTStrSpaceDestroy(&pThis->SelfEntry.Attributes, rtManifestDestroyAttribute, pThis); 212 267 RTMemFree(pThis); 213 268 } … … 238 293 239 294 295 /** 296 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation.} 297 */ 298 static DECLCALLBACK(int) rtManifestAttributeClearVisited(PRTSTRSPACECORE pStr, void *pvUser) 299 { 300 PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore); 301 pAttr->fVisited = false; 302 return 0; 303 } 304 305 306 /** 307 * @callback_method_impl{FNRTSTRSPACECALLBACK, Prepare equals operation.} 308 */ 309 static DECLCALLBACK(int) rtManifestEntryClearVisited(PRTSTRSPACECORE pStr, void *pvUser) 310 { 311 PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore); 312 RTStrSpaceEnumerate(&pEntry->Attributes, rtManifestAttributeClearVisited, NULL); 313 pEntry->fVisited = false; 314 return 0; 315 } 316 317 318 /** 319 * @callback_method_impl{FNRTSTRSPACECALLBACK, Finds the first missing.} 320 */ 321 static 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->papszIgnoreAttr; 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 */ 362 static 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 */ 398 static 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->papszIgnoreAttr; 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 (strcmp(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 */ 476 DECLINLINE (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 */ 505 static 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 RTStrPrintf(pEquals->pszError, pEquals->cbError, "'%s' not found in the 2nd manifest", pEntry1->StrCore.pszString); 541 return VERR_NOT_EQUAL; 542 } 543 544 Assert(!pEntry1->fVisited); 545 Assert(!pEntry2->fVisited); 546 pEntry1->fVisited = true; 547 pEntry2->fVisited = true; 548 pEquals->cEntries2++; 549 550 return rtManifestEntryCompare2(pEquals, pEntry1, pEntry2); 551 } 552 553 554 240 555 RTDECL(int) RTManifestEqualsEx(RTMANIFEST hManifest1, RTMANIFEST hManifest2, const char * const *papszIgnoreEntries, 241 const char * const *papszIgnoreAttr, char *pszEntry, size_t cbEntry)556 const char * const *papszIgnoreAttr, uint32_t fFlags, char *pszError, size_t cbError) 242 557 { 243 558 /* 244 559 * Validate input. 245 560 */ 246 AssertPtrNullReturn(pszE ntry, VERR_INVALID_POINTER);247 if (pszE ntry && cbEntry)248 *pszE ntry= '\0';561 AssertPtrNullReturn(pszError, VERR_INVALID_POINTER); 562 if (pszError && cbError) 563 *pszError = '\0'; 249 564 RTMANIFESTINT *pThis1 = hManifest1; 250 RTMANIFESTINT *pThis2 = hManifest 1;565 RTMANIFESTINT *pThis2 = hManifest2; 251 566 if (pThis1 != NIL_RTMANIFEST) 252 567 { … … 259 574 AssertReturn(pThis2->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE); 260 575 } 576 AssertReturn(!(fFlags & ~(RTMANIFEST_EQUALS_IGN_MISSING_ATTRS)), VERR_INVALID_PARAMETER); 261 577 262 578 /* … … 269 585 270 586 /* 271 * 272 */ 273 274 275 /** @todo implement comparing. */ 276 277 return VERR_NOT_IMPLEMENTED; 587 * Since we have to use callback style enumeration, we have to mark the 588 * entries and attributes to make sure we've covered them all. 589 */ 590 RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryClearVisited, NULL); 591 RTStrSpaceEnumerate(&pThis2->Entries, rtManifestEntryClearVisited, NULL); 592 RTStrSpaceEnumerate(&pThis1->SelfEntry.Attributes, rtManifestAttributeClearVisited, NULL); 593 RTStrSpaceEnumerate(&pThis2->SelfEntry.Attributes, rtManifestAttributeClearVisited, NULL); 594 595 RTMANIFESTEQUALS Equals; 596 Equals.pThis2 = pThis2; 597 Equals.fFlags = fFlags; 598 Equals.papszIgnoreEntries = papszIgnoreEntries; 599 Equals.papszIgnoreAttr = papszIgnoreAttr; 600 Equals.pszError = pszError; 601 Equals.cbError = cbError; 602 603 Equals.cIgnoredEntries2 = 0; 604 Equals.cEntries2 = 0; 605 Equals.cIgnoredAttributes1 = 0; 606 Equals.cIgnoredAttributes2 = 0; 607 Equals.cAttributes2 = 0; 608 Equals.pAttributes2 = NULL; 609 Equals.pszCurEntry = NULL; 610 611 int rc = rtManifestEntryCompare2(&Equals, &pThis1->SelfEntry, &pThis2->SelfEntry); 612 if (RT_SUCCESS(rc)) 613 rc = RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryCompare, &Equals); 614 if (RT_SUCCESS(rc)) 615 { 616 /* 617 * Did we cover all entries of the 2nd manifest? 618 */ 619 if (Equals.cEntries2 + Equals.cIgnoredEntries2 != pThis2->cEntries) 620 rc = RTStrSpaceEnumerate(&pThis1->Entries, rtManifestEntryFindMissing2, &Equals); 621 } 622 623 return rc; 278 624 } 279 625 … … 281 627 RTDECL(int) RTManifestEquals(RTMANIFEST hManifest1, RTMANIFEST hManifest2) 282 628 { 283 return RTManifestEqualsEx(hManifest1, hManifest2, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttrs*/, NULL, 0); 629 return RTManifestEqualsEx(hManifest1, hManifest2, 630 NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttrs*/, 631 0 /*fFlags*/, NULL, 0); 284 632 } 285 633 … … 289 637 * 290 638 * @returns IPRT status code. 291 * @param p Attributes The attribute container.639 * @param pEntry Pointer to the entry. 292 640 * @param pszAttr The name of the attribute to add. 293 641 * @param pszValue The value string. 294 642 * @param fType The attribute type type. 295 643 */ 296 static int rtManifestSetAttrWorker(PRT STRSPACE pAttributes, const char *pszAttr, const char *pszValue, uint32_t fType)644 static int rtManifestSetAttrWorker(PRTMANIFESTENTRY pEntry, const char *pszAttr, const char *pszValue, uint32_t fType) 297 645 { 298 646 char *pszValueCopy; … … 305 653 */ 306 654 AssertCompileMemberOffset(RTMANIFESTATTR, StrCore, 0); 307 PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)RTStrSpaceGet( pAttributes, pszAttr);655 PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)RTStrSpaceGet(&pEntry->Attributes, pszAttr); 308 656 if (pAttr) 309 657 { … … 326 674 pAttr->pszValue = pszValueCopy; 327 675 pAttr->fType = fType; 328 if (RT_UNLIKELY(!RTStrSpaceInsert( pAttributes, &pAttr->StrCore)))676 if (RT_UNLIKELY(!RTStrSpaceInsert(&pEntry->Attributes, &pAttr->StrCore))) 329 677 { 330 678 AssertFailed(); … … 333 681 return VERR_INTERNAL_ERROR_4; 334 682 } 683 pEntry->cAttributes++; 335 684 } 336 685 … … 359 708 AssertReturn(RT_IS_POWER_OF_TWO(fType) && fType < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER); 360 709 361 return rtManifestSetAttrWorker(&pThis-> Attributes, pszAttr, pszValue, fType);710 return rtManifestSetAttrWorker(&pThis->SelfEntry, pszAttr, pszValue, fType); 362 711 } 363 712 … … 367 716 * 368 717 * @returns IPRT status code. 369 * @param p Attributes The attribute container.718 * @param pEntry Pointer to the entry. 370 719 * @param pszAttr The name of the attribute to remove. 371 720 */ 372 static int rtManifestUnsetAttrWorker(PRT STRSPACE pAttributes, const char *pszAttr)373 { 374 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove( pAttributes, pszAttr);721 static int rtManifestUnsetAttrWorker(PRTMANIFESTENTRY pEntry, const char *pszAttr) 722 { 723 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&pEntry->Attributes, pszAttr); 375 724 if (!pStrCore) 376 725 return VWRN_NOT_FOUND; 726 pEntry->cAttributes--; 377 727 rtManifestDestroyAttribute(pStrCore, NULL); 378 728 return VINF_SUCCESS; … … 396 746 AssertPtr(pszAttr); 397 747 398 return rtManifestUnsetAttrWorker(&pThis-> Attributes, pszAttr);748 return rtManifestUnsetAttrWorker(&pThis->SelfEntry, pszAttr); 399 749 } 400 750 … … 420 770 rc = RTStrGetCpEx(&pszCur, &uc); 421 771 if (RT_FAILURE(rc)) 422 break;772 return rc; 423 773 if (!uc) 424 774 break; … … 426 776 fNeedNormalization = true; 427 777 else if (uc < 32 || uc == ':' || uc == '(' || uc == ')') 428 { 429 rc = VERR_INVALID_NAME; 430 break; 431 } 778 return VERR_INVALID_NAME; 432 779 } 433 780 434 781 if (pfNeedNormalization) 435 782 *pfNeedNormalization = fNeedNormalization; 783 784 size_t cchEntry = pszCur - pszEntry - 1; 785 if (!cchEntry) 786 rc = VERR_INVALID_NAME; 436 787 if (pcchEntry) 437 *pcchEntry = pszCur - pszEntry - 1; 788 *pcchEntry = cchEntry; 789 438 790 return rc; 439 791 } … … 547 899 return VERR_INTERNAL_ERROR_4; 548 900 } 901 pThis->cEntries++; 549 902 } 550 903 else if (RT_FAILURE(rc)) 551 904 return rc; 552 905 553 return rtManifestSetAttrWorker( &pEntry->Attributes, pszAttr, pszValue, fType);906 return rtManifestSetAttrWorker(pEntry, pszAttr, pszValue, fType); 554 907 } 555 908 … … 584 937 rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry); 585 938 if (RT_SUCCESS(rc)) 586 rc = rtManifestUnsetAttrWorker( &pEntry->Attributes, pszAttr);939 rc = rtManifestUnsetAttrWorker(pEntry, pszAttr); 587 940 return rc; 588 941 } … … 640 993 641 994 if (RTStrSpaceInsert(&pThis->Entries, &pEntry->StrCore)) 995 { 996 pThis->cEntries++; 642 997 rc = VINF_SUCCESS; 998 } 643 999 else 644 1000 { … … 685 1041 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&pThis->Entries, pEntry->StrCore.pszString); 686 1042 AssertReturn(pStrCore, VERR_INTERNAL_ERROR_3); 1043 pThis->cEntries--; 687 1044 rtManifestDestroyEntry(pStrCore, pThis); 688 1045 } … … 961 1318 Args.hVfsIos = hVfsIos; 962 1319 Args.pszEntry = "main"; 963 int rc = RTStrSpaceEnumerate(&pThis-> Attributes, rtManifestWriteStdAttr, &Args);1320 int rc = RTStrSpaceEnumerate(&pThis->SelfEntry.Attributes, rtManifestWriteStdAttr, &Args); 964 1321 if (RT_SUCCESS(rc)) 965 1322 rc = RTStrSpaceEnumerate(&pThis->Entries, rtManifestWriteStdEntry, hVfsIos);
Note:
See TracChangeset
for help on using the changeset viewer.