ManifestInternal.cpp
1 /* 2 * Copyright (c) 2004-2006,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25 26 #include <stdlib.h> 27 #include <sys/stat.h> 28 #include <string.h> 29 #include <sys/types.h> 30 #include <dirent.h> 31 #include <unistd.h> 32 #include <sys/param.h> 33 #include <sys/mount.h> 34 #include <sys/uio.h> 35 #include <security_utilities/cfutilities.h> 36 #include <fts.h> 37 #include <fcntl.h> 38 #include <CommonCrypto/CommonDigest.h> 39 40 #include "Manifest.h" 41 42 ModuleNexus<CSSMInitializer> CSSMInitializer::mInstance; 43 44 CSSMInitializer::CSSMInitializer () : mModule (gGuidAppleCSP), mCSP (mModule) 45 { 46 } 47 48 49 50 CSSMInitializer::~CSSMInitializer () 51 { 52 } 53 54 55 56 CssmClient::CSP* CSSMInitializer::GetCSP () 57 { 58 return &mInstance().mCSP; 59 } 60 61 62 63 //========================== MANIFEST ITEM LIST ========================== 64 65 66 67 ManifestItemList::ManifestItemList () 68 { 69 } 70 71 72 73 ManifestItemList::~ManifestItemList () 74 { 75 // throw away all of the items in the list 76 iterator it = begin (); 77 while (it != end ()) 78 { 79 delete *it++; 80 } 81 } 82 83 84 85 // return the path portion of a URL after checking to see if we support its scheme 86 void ManifestItemList::DecodeURL (CFURLRef url, char *pathBuffer, CFIndex maxBufLen) 87 { 88 // get the scheme from the url and check to make sure it is a "file" scheme 89 CFRef<CFStringRef> scheme (CFURLCopyScheme (url)); 90 if (CFStringCompare (scheme, CFSTR("file"), 0) != 0) 91 { 92 // we only support file URL's 93 MacOSError::throwMe (errSecManifestNotSupported); 94 } 95 96 // convert the url into a "real" path name 97 if (!CFURLGetFileSystemRepresentation (url, false, (UInt8*) pathBuffer, maxBufLen)) 98 { 99 MacOSError::throwMe (errSecManifestNotEqual); 100 } 101 } 102 103 104 105 void ManifestItemList::AddFileSystemObject (char* path, StringSet& exceptions, bool isRoot, bool hasAppleDoubleResourceFork) 106 { 107 // see if our path is in the exception list. If it is, do nothing else 108 StringSet::iterator it = exceptions.find (path); 109 if (it != exceptions.end ()) 110 { 111 secinfo ("manifest", "Did not add %s to the manifest.", path); 112 return; 113 } 114 115 // now that we have the path, do a stat and see what we have 116 struct stat nodeStat; 117 int result = lstat (path, &nodeStat); 118 UnixError::check (result); 119 120 FileSystemEntryItem* mItem; 121 122 bool includeUserAndGroup = true; 123 124 switch (nodeStat.st_mode & S_IFMT) 125 { 126 case S_IFDIR: // are we a directory? 127 { 128 ManifestDirectoryItem* dirItem = new ManifestDirectoryItem (); 129 dirItem->SetPath (path, exceptions, isRoot); 130 mItem = dirItem; 131 } 132 break; 133 134 case S_IFREG: 135 { 136 ManifestFileItem* fileItem = new ManifestFileItem (); 137 fileItem->SetPath (path); 138 fileItem->ComputeRepresentations (nodeStat, hasAppleDoubleResourceFork); 139 mItem = fileItem; 140 } 141 break; 142 143 case S_IFLNK: 144 { 145 ManifestSymLinkItem* symItem = new ManifestSymLinkItem (); 146 symItem->SetPath (path); 147 symItem->ComputeRepresentation (); 148 mItem = symItem; 149 nodeStat.st_mode = S_IFLNK; 150 includeUserAndGroup = false; 151 } 152 break; 153 154 default: 155 { 156 ManifestOtherItem* otherItem = new ManifestOtherItem (); 157 otherItem->SetPath (path); 158 mItem = otherItem; 159 } 160 break; 161 } 162 163 if (includeUserAndGroup) // should we set the info? 164 { 165 mItem->SetUID (nodeStat.st_uid); 166 mItem->SetGID (nodeStat.st_gid); 167 } 168 169 mItem->SetMode (nodeStat.st_mode); 170 171 push_back (mItem); 172 } 173 174 175 176 void ManifestItemList::AddDataObject (CFDataRef object) 177 { 178 // reconstruct the pointer 179 SHA1Digest digest; 180 CC_SHA1_CTX digestContext; 181 182 CC_SHA1_Init (&digestContext); 183 184 const UInt8* data = CFDataGetBytePtr (object); 185 CFIndex length = CFDataGetLength (object); 186 187 CC_SHA1_Update (&digestContext, data, (CC_LONG)length); 188 CC_SHA1_Final (digest, &digestContext); 189 190 ManifestDataBlobItem* db = new ManifestDataBlobItem (); 191 192 db->SetDigest (&digest); 193 db->SetLength (length); 194 195 push_back (db); 196 } 197 198 199 200 void ManifestItemList::ConvertToStringSet (const char* path, CFArrayRef exceptionList, StringSet &exceptions) 201 { 202 if (exceptionList != NULL) 203 { 204 std::string prefix = path; 205 206 // put us in canonical form 207 if (prefix[prefix.length () - 1] != '/') 208 { 209 prefix += '/'; 210 } 211 212 // enumerate the list 213 CFIndex max = CFArrayGetCount (exceptionList); 214 CFIndex n; 215 216 for (n = 0; n < max; ++n) 217 { 218 CFTypeRef dataRef = CFArrayGetValueAtIndex (exceptionList, n); 219 if (CFGetTypeID (dataRef) != CFStringGetTypeID ()) 220 { 221 MacOSError::throwMe (errSecManifestInvalidException); 222 } 223 224 // always prepend the prefix -- the spec says that all items in the exception list are relative to the root 225 std::string s = prefix + cfString (CFStringRef (dataRef)); 226 secinfo ("manifest", "Uncanonicalized path is %s", s.c_str ()); 227 228 // canonicalize the path and insert if successful. 229 char realPath [PATH_MAX]; 230 if (realpath (s.c_str (), realPath) != NULL) 231 { 232 secinfo ("manifest", "Inserted path %s as an exception", realPath); 233 exceptions.insert (realPath); 234 } 235 } 236 } 237 } 238 239 240 241 void ManifestItemList::AddObject (CFTypeRef object, CFArrayRef exceptionList) 242 { 243 // get the type of the object 244 CFTypeID objectID = CFGetTypeID (object); 245 246 if (objectID == CFDataGetTypeID ()) 247 { 248 AddDataObject ((CFDataRef) object); 249 } 250 else if (objectID == CFURLGetTypeID ()) 251 { 252 StringSet exceptions; 253 254 // get the path from the URL 255 char path [PATH_MAX]; 256 DecodeURL ((CFURLRef) object, path, sizeof (path)); 257 258 // canonicalize 259 char realPath [PATH_MAX]; 260 if (realpath (path, realPath) == NULL) 261 { 262 UnixError::throwMe (); 263 } 264 265 ConvertToStringSet (realPath, exceptionList, exceptions); 266 267 AddFileSystemObject (realPath, exceptions, true, false); 268 } 269 else 270 { 271 MacOSError::throwMe (errSecManifestNotEqual); 272 } 273 } 274 275 276 277 void RootItemList::Compare (RootItemList& item, bool compareOwnerAndGroup) 278 { 279 // the number of items in the list has to be the same 280 unsigned numItems = (unsigned)size (); 281 282 if (numItems != item.size ()) 283 { 284 MacOSError::throwMe (errSecManifestNotEqual); 285 } 286 287 // for a root item list, items in the manifest MUST have the same creation order 288 unsigned i; 289 290 for (i = 0; i < numItems; ++i) 291 { 292 ManifestItem* item1 = (*this)[i]; 293 ManifestItem* item2 = item[i]; 294 295 if (item1->GetItemType () != item2->GetItemType ()) 296 { 297 MacOSError::throwMe (errSecManifestNotEqual); 298 } 299 300 item1->Compare (item2, compareOwnerAndGroup); 301 } 302 } 303 304 305 306 class CompareManifestFileItems 307 { 308 public: 309 bool operator () (ManifestItem *a, ManifestItem *b); 310 }; 311 312 313 314 bool CompareManifestFileItems::operator () (ManifestItem *a, ManifestItem *b) 315 { 316 FileSystemEntryItem *aa = static_cast<FileSystemEntryItem*>(a); 317 FileSystemEntryItem *bb = static_cast<FileSystemEntryItem*>(b); 318 319 return strcmp (aa->GetName (), bb->GetName ()) < 0; 320 } 321 322 323 324 void FileSystemItemList::Compare (FileSystemItemList &a, bool compareOwnerAndGroup) 325 { 326 unsigned numItems = (unsigned)size (); 327 328 if (numItems != a.size ()) 329 { 330 MacOSError::throwMe (errSecManifestNotEqual); 331 } 332 333 // sort the two lists 334 sort (begin (), end (), CompareManifestFileItems ()); 335 sort (a.begin (), a.end (), CompareManifestFileItems ()); 336 337 // compare each item in the list 338 unsigned i; 339 for (i = 0; i < numItems; ++i) 340 { 341 ManifestItem *thisListPtr = (*this)[i]; 342 ManifestItem *aListPtr = a[i]; 343 if (thisListPtr->GetItemType () != aListPtr->GetItemType ()) 344 { 345 MacOSError::throwMe (errSecManifestNotEqual); 346 } 347 thisListPtr->Compare (aListPtr, compareOwnerAndGroup); 348 } 349 } 350 351 352 353 //========================== MANIFEST ========================== 354 355 356 357 ManifestInternal::ManifestInternal () 358 { 359 } 360 361 362 363 ManifestInternal::~ManifestInternal () 364 { 365 secinfo ("manifest", "Destroyed manifest internal %p", this); 366 } 367 368 369 370 void ManifestInternal::CompareManifests (ManifestInternal& m1, ManifestInternal& m2, SecManifestCompareOptions options) 371 { 372 if ((options & ~kSecManifestVerifyOwnerAndGroup) != 0) 373 { 374 MacOSError::throwMe (errSecUnimplemented); // we don't support these options 375 } 376 377 m1.mManifestItems.Compare (m2.mManifestItems, (bool) options & kSecManifestVerifyOwnerAndGroup); 378 } 379 380 381 382 //========================== MANIFEST ITEM ========================== 383 ManifestItem::~ManifestItem () 384 { 385 } 386 387 388 389 //========================== DATA BLOB ITEM ========================== 390 ManifestDataBlobItem::ManifestDataBlobItem () 391 { 392 } 393 394 395 396 ManifestDataBlobItem::~ManifestDataBlobItem () 397 { 398 } 399 400 401 402 ManifestItemType ManifestDataBlobItem::GetItemType () 403 { 404 return kManifestDataBlobItemType; 405 } 406 407 408 409 const SHA1Digest* ManifestDataBlobItem::GetDigest () 410 { 411 return &mSHA1Digest; 412 } 413 414 415 416 void ManifestDataBlobItem::SetDigest (const SHA1Digest *sha1Digest) 417 { 418 memcpy (&mSHA1Digest, sha1Digest, sizeof (SHA1Digest)); 419 } 420 421 422 423 size_t ManifestDataBlobItem::GetLength () 424 { 425 return mLength; 426 } 427 428 429 430 void ManifestDataBlobItem::SetLength (size_t length) 431 { 432 mLength = length; 433 } 434 435 436 437 void ManifestDataBlobItem::Compare (ManifestItem* item, bool compareOwnerAndGroup) 438 { 439 ManifestDataBlobItem* i = static_cast<ManifestDataBlobItem*>(item); 440 if (memcmp (&i->mSHA1Digest, &mSHA1Digest, sizeof (SHA1Digest)) != 0) 441 { 442 MacOSError::throwMe (errSecManifestNotEqual); 443 } 444 } 445 446 447 448 //========================== FILE SYSTEM ENTRY ITEM ========================== 449 450 451 452 FileSystemEntryItem::FileSystemEntryItem () : mUserID (0), mGroupID (0), mMode (0) 453 { 454 } 455 456 457 458 FileSystemEntryItem::~FileSystemEntryItem () 459 { 460 } 461 462 463 464 void FileSystemEntryItem::SetName (char* name) 465 { 466 mName = name; 467 } 468 469 470 471 static char* StringTail (char* path) 472 { 473 char* finger = path + strlen (path) - 1; 474 while (finger != path && *finger != '/') 475 { 476 finger -= 1; 477 } 478 479 if (finger != path) // did find a separator 480 { 481 finger += 1; 482 } 483 484 return finger; 485 } 486 487 488 489 void FileSystemEntryItem::SetPath (char* path) 490 { 491 // save off the path 492 mPath = path; 493 494 // while we are at it, extract that last name of the path name and save it off as the name 495 mName = StringTail (path); 496 secinfo ("manifest", "Created file item for %s with name %s", mPath.c_str (), mName.c_str ()); 497 } 498 499 500 501 void FileSystemEntryItem::SetUID (uid_t uid) 502 { 503 mUserID = uid; 504 } 505 506 507 508 void FileSystemEntryItem::SetGID (gid_t gid) 509 { 510 mGroupID = gid; 511 } 512 513 514 515 void FileSystemEntryItem::SetMode (mode_t mode) 516 { 517 mMode = mode; 518 } 519 520 521 522 uid_t FileSystemEntryItem::GetUID () const 523 { 524 return mUserID; 525 } 526 527 528 gid_t FileSystemEntryItem::GetGID () const 529 { 530 return mGroupID; 531 } 532 533 534 535 mode_t FileSystemEntryItem::GetMode () const 536 { 537 return mMode; 538 } 539 540 541 542 const char* FileSystemEntryItem::GetName () const 543 { 544 return (char*) mName.c_str (); 545 } 546 547 548 549 void FileSystemEntryItem::Compare (ManifestItem *aa, bool compareOwnerAndGroup) 550 { 551 FileSystemEntryItem* a = static_cast<FileSystemEntryItem*>(aa); 552 553 if (mName != a->mName || mMode != a->mMode) 554 { 555 MacOSError::throwMe (errSecManifestNotEqual); 556 } 557 558 if (compareOwnerAndGroup) 559 { 560 if (mUserID != a->mUserID || mGroupID != a->mGroupID) 561 { 562 MacOSError::throwMe (errSecManifestNotEqual); 563 } 564 } 565 } 566 567 568 569 //========================== MANIFEST FILE ITEM ========================== 570 571 572 573 bool ManifestFileItem::FileSystemHasTrueForks (char* pathToFile) 574 { 575 // return true if volume to which path points supports true forked files 576 struct statfs st; 577 int result = statfs (pathToFile, &st); 578 if (result != 0) 579 { 580 secinfo ("manifest", "Could not get statfs (error was %s)", strerror (errno)); 581 UnixError::throwMe (); 582 } 583 584 return strcmp (st.f_fstypename, "afpfs") == 0 || strcmp (st.f_fstypename, "hfs") == 0; 585 } 586 587 588 589 std::string ManifestFileItem::ResourceFileName (char* path) 590 { 591 std::string filePath; 592 593 if (FileSystemHasTrueForks (path)) 594 { 595 filePath = path; 596 597 return filePath + "/rsrc"; 598 } 599 else 600 { 601 return ""; 602 } 603 } 604 605 606 607 bool ManifestFileItem::HasResourceFork (char* pathToFile, std::string &result, struct stat &st) 608 { 609 // try to get the stat on the file. If it works, the file exists 610 result = ResourceFileName (pathToFile); 611 if (result.length () != 0) 612 { 613 int stresult = lstat (result.c_str (), &st); 614 if (stresult == 0) 615 { 616 return st.st_size != 0; 617 } 618 } 619 620 return false; 621 } 622 623 624 625 ManifestFileItem::ManifestFileItem () : mNumForks (1) 626 { 627 } 628 629 630 631 ManifestFileItem::~ManifestFileItem () 632 { 633 secinfo ("manifest", "Destroyed manifest item %p for path %s", this, mPath.c_str ()); 634 } 635 636 637 638 ManifestItemType ManifestFileItem::GetItemType () 639 { 640 return kManifestFileItemType; 641 } 642 643 644 645 u_int32_t ManifestFileItem::GetNumberOfForks () 646 { 647 return mNumForks; 648 } 649 650 651 652 void ManifestFileItem::SetNumberOfForks (u_int32_t numForks) 653 { 654 mNumForks = numForks; 655 } 656 657 658 659 bool ManifestFileItem::FileIsMachOBinary (char* path) 660 { 661 return false; 662 } 663 664 665 666 void ManifestFileItem::SetForkLength (int which, size_t length) 667 { 668 mFileLengths[which] = length; 669 } 670 671 672 673 size_t ManifestFileItem::GetForkLength (int which) 674 { 675 return mFileLengths[which]; 676 } 677 678 679 680 void ManifestFileItem::ComputeRepresentations (struct stat &st, bool hasAppleDoubleResourceFork) 681 { 682 // digest the data fork 683 mNumForks = 1; 684 ComputeDigestForFile ((char*) mPath.c_str (), mDigest[0], mFileLengths[0], st); 685 686 struct stat stat2; 687 std::string resourceForkName; 688 if (hasAppleDoubleResourceFork) 689 { 690 mNumForks = 2; 691 692 resourceForkName = mPath; 693 // walk back to find the beginning of the path and insert ._ 694 int i = (int)resourceForkName.length () - 1; 695 while (i >= 0 && resourceForkName[i] != '/') 696 { 697 i -= 1; 698 } 699 700 i += 1; 701 702 resourceForkName.insert (i, "._"); 703 704 ComputeDigestForAppleDoubleResourceFork ((char*) resourceForkName.c_str(), mDigest[1], mFileLengths[1]); 705 } 706 else if (HasResourceFork ((char*) mPath.c_str (), resourceForkName, stat2)) 707 { 708 mNumForks = 2; 709 ComputeDigestForFile ((char*) resourceForkName.c_str (), mDigest[1], mFileLengths[1], stat2); 710 } 711 } 712 713 714 715 static const int kReadChunkSize = 4096 * 4; 716 717 718 719 static u_int32_t ExtractUInt32 (u_int8_t *&finger) 720 { 721 u_int32_t result = 0; 722 int i; 723 for (i = 0; i < 4; ++i) 724 { 725 result = (result << 8) | *finger++; 726 } 727 728 return result; 729 } 730 731 732 733 void ManifestFileItem::ComputeDigestForAppleDoubleResourceFork (char* name, SHA1Digest &digest, size_t &fileLength) 734 { 735 secinfo ("manifest", "Creating digest for AppleDouble resource fork %s", name); 736 737 CC_SHA1_CTX digestContext; 738 CC_SHA1_Init (&digestContext); 739 740 // bring the file into memory 741 int fileNo = open (name, O_RDONLY, 0); 742 if (fileNo == -1) 743 { 744 UnixError::throwMe (); 745 } 746 747 // figure out how big the file is. 748 struct stat st; 749 int result = fstat (fileNo, &st); 750 if (result == -1) 751 { 752 UnixError::throwMe (); 753 } 754 755 u_int8_t *buffer = new u_int8_t[st.st_size]; 756 ssize_t bytesRead = read (fileNo, buffer, (size_t)st.st_size); 757 close (fileNo); 758 759 if (bytesRead != st.st_size) 760 { 761 delete[] buffer; 762 UnixError::throwMe (); 763 } 764 765 // walk the entry table to find the offset to our resource fork 766 u_int8_t *bufPtr = buffer + 24; // size of the header + filler 767 768 // compute the number of entries in the file 769 int numEntries = (((int) bufPtr[0]) << 8) | (int) (bufPtr [1]); 770 bufPtr += 2; 771 772 ssize_t length = 0; 773 ssize_t offset = 0; 774 775 int i; 776 for (i = 0; i < numEntries; ++i) 777 { 778 // bufPtr points to an entry descriptor. Four bytes for the ID, four for the offset, four for the length 779 ssize_t id = ExtractUInt32 (bufPtr); 780 offset = ExtractUInt32 (bufPtr); 781 length = ExtractUInt32 (bufPtr); 782 783 if (id == 2) // is it the resource fork? 784 { 785 break; 786 } 787 } 788 789 if (i >= numEntries) // did we run off the end? This had better not happen 790 { 791 MacOSError::throwMe (errSecManifestNotSupported); 792 } 793 794 fileLength = length; 795 796 // digest the data 797 CC_SHA1_Update (&digestContext, buffer + offset, (CC_LONG)length); 798 799 // compute the SHA1 hash 800 CC_SHA1_Final (digest, &digestContext); 801 802 delete[] buffer; 803 } 804 805 806 807 void ManifestFileItem::ComputeDigestForFile (char* name, SHA1Digest &digest, size_t &fileLength, struct stat &st) 808 { 809 secinfo ("manifest", "Creating digest for %s", name); 810 811 // create a context for the digest operation 812 CC_SHA1_CTX digestContext; 813 CC_SHA1_Init (&digestContext); 814 815 816 int fileNo = open (name, O_RDONLY, 0); 817 if (fileNo == -1) 818 { 819 UnixError::throwMe (); 820 } 821 822 fileLength = (size_t)st.st_size; 823 824 if (st.st_size != 0) 825 { 826 // read the file 827 std::vector<char> buffer(kReadChunkSize); 828 829 ssize_t bytesRead; 830 while ((bytesRead = read (fileNo, buffer.data(), kReadChunkSize)) != 0) 831 { 832 // digest the read data 833 CC_SHA1_Update (&digestContext, buffer.data(), (CC_LONG)bytesRead); 834 } 835 836 // compute the SHA1 hash 837 CC_SHA1_Final (digest, &digestContext); 838 } 839 840 close (fileNo); 841 } 842 843 844 845 void ManifestFileItem::GetItemRepresentation (int whichFork, void* &itemRep, size_t &size) 846 { 847 itemRep = (void*) &mDigest[whichFork]; 848 size = kSHA1DigestSize; 849 } 850 851 852 853 void ManifestFileItem::SetItemRepresentation (int whichFork, const void* itemRep, size_t size) 854 { 855 memcpy ((void*) &mDigest[whichFork], itemRep, size); 856 } 857 858 859 860 void ManifestFileItem::Compare (ManifestItem *manifestItem, bool compareOwnerAndGroup) 861 { 862 FileSystemEntryItem::Compare (manifestItem, compareOwnerAndGroup); 863 864 ManifestFileItem* item = static_cast< ManifestFileItem*>(manifestItem); 865 866 secinfo ("manifest", "Comparing file item %s against %s", GetName (), item->GetName ()); 867 868 // the number of forks should be equal 869 if (mNumForks != item->mNumForks) 870 { 871 MacOSError::throwMe (errSecManifestNotEqual); 872 } 873 874 // compare file lengths 875 int i; 876 for (i = 0; i < mNumForks; ++i) 877 { 878 if (mFileLengths[i] != item->mFileLengths[i]) 879 { 880 MacOSError::throwMe (errSecManifestNotEqual); 881 } 882 883 if (memcmp (&mDigest[i], item->mDigest[i], kSHA1DigestSize) != 0) 884 { 885 MacOSError::throwMe (errSecManifestNotEqual); 886 } 887 } 888 } 889 890 891 892 //========================== MANIFEST DIRECTORY ITEM ========================== 893 894 895 896 ManifestDirectoryItem::ManifestDirectoryItem () 897 { 898 } 899 900 901 902 ManifestDirectoryItem::~ManifestDirectoryItem () 903 { 904 secinfo ("manifest", "Destroyed directory item %p for path %s", this, mPath.c_str ()); 905 } 906 907 908 const char* kAppleDoublePrefix = "._"; 909 const int kAppleDoublePrefixLength = 2; 910 911 static int CompareFilenames (const FTSENT** a, const FTSENT** b) 912 { 913 // ._name is always greater than name 914 // otherwise, ._ is ignored for sorting purposes 915 const char* aa = (*a)->fts_name; 916 const char* bb = (*b)->fts_name; 917 bool aHasPrefix = false; 918 919 if (strncmp (aa, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix? 920 { 921 aHasPrefix = true; 922 aa += kAppleDoublePrefixLength; 923 } 924 925 if (strncmp (bb, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix? 926 { 927 bb += kAppleDoublePrefixLength; 928 } 929 930 int compare = strcmp (aa, bb); 931 932 if (compare == 0 && aHasPrefix) 933 { 934 return 1; 935 } 936 937 return compare; 938 } 939 940 941 942 const u_int8_t kAppleDoubleMagicNumber[] = {0x00, 0x05, 0x16, 0x07}; 943 944 945 946 static bool PathIsAppleDoubleFile (const char* path) 947 { 948 // Open the file and check the "magic number". 949 int fRef = open (path, O_RDONLY, 0); 950 951 u_int8_t buffer[4]; 952 953 // read the first four bytes of the file 954 ssize_t bytesRead = read(fRef, buffer, 4); 955 if (bytesRead == -1) 956 { 957 int err = errno; 958 close (fRef); 959 UnixError::throwMe (err); 960 } 961 962 close (fRef); 963 964 if (bytesRead != 4) // did we get enough bytes? 965 { 966 return false; 967 } 968 969 // what we got had better be the proper magic number for this file type 970 int i; 971 for (i = 0; i < 4; ++i) 972 { 973 if (buffer[i] != kAppleDoubleMagicNumber[i]) 974 { 975 return false; 976 } 977 } 978 979 return true; 980 } 981 982 983 984 void ManifestDirectoryItem::SetPath (char* path, StringSet &exceptions, bool isRoot) 985 { 986 if (isRoot) 987 { 988 mName = "/"; 989 mPath = path; 990 } 991 else 992 { 993 FileSystemEntryItem::SetPath (path); 994 } 995 996 secinfo ("manifest", "Added directory entry for %s with name %s", mPath.c_str (), mName.c_str ()); 997 998 // enumerate the contents of the directory. 999 char* path_argv[] = { path, NULL }; 1000 FTS* thisDir = fts_open (path_argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV, CompareFilenames); 1001 if (thisDir == NULL) // huh? The file disappeared or isn't a directory any more 1002 { 1003 UnixError::throwMe (); 1004 } 1005 1006 (void)fts_read(thisDir); 1007 FTSENT* dirEnt = fts_children (thisDir, FTS_NAMEONLY); 1008 1009 while (dirEnt != NULL) 1010 { 1011 // get the next entry 1012 FTSENT* dirEntNext = dirEnt->fts_link; 1013 bool hasAppleDoubleResourceFork = false; 1014 1015 // see if it is an AppleDouble resource fork for this file 1016 if (dirEntNext && 1017 strncmp (dirEntNext->fts_name, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0 && 1018 strcmp (dirEnt->fts_name, dirEntNext->fts_name + kAppleDoublePrefixLength) == 0) 1019 { 1020 if (PathIsAppleDoubleFile ((mPath + "/" + dirEntNext->fts_name).c_str ())) 1021 { 1022 hasAppleDoubleResourceFork = true; 1023 dirEntNext = dirEntNext->fts_link; 1024 } 1025 } 1026 1027 // figure out what this is pointing to. 1028 std::string fileName = mPath + "/" + dirEnt->fts_name; 1029 1030 mDirectoryItems.AddFileSystemObject ((char*) fileName.c_str(), exceptions, false, hasAppleDoubleResourceFork); 1031 1032 dirEnt = dirEntNext; 1033 } 1034 1035 fts_close(thisDir); 1036 } 1037 1038 1039 1040 ManifestItemType ManifestDirectoryItem::GetItemType () 1041 { 1042 return kManifestDirectoryItemType; 1043 } 1044 1045 1046 1047 void ManifestDirectoryItem::Compare (ManifestItem* a, bool compareOwnerAndGroup) 1048 { 1049 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1050 ManifestDirectoryItem* aa = static_cast<ManifestDirectoryItem*>(a); 1051 secinfo ("manifest", "Comparing directory item %s against %s", GetName (), aa->GetName ()); 1052 mDirectoryItems.Compare (aa->mDirectoryItems, compareOwnerAndGroup); 1053 } 1054 1055 1056 1057 //========================== MANIFEST SYMLINK ITEM ========================== 1058 1059 1060 1061 ManifestSymLinkItem::ManifestSymLinkItem () 1062 { 1063 } 1064 1065 1066 1067 ManifestSymLinkItem::~ManifestSymLinkItem () 1068 { 1069 secinfo ("manifest", "Destroyed symlink item for %s", mPath.c_str ()); 1070 } 1071 1072 1073 1074 void ManifestSymLinkItem::ComputeRepresentation () 1075 { 1076 char path [FILENAME_MAX]; 1077 int result = (int)readlink (mPath.c_str (), path, sizeof (path)); 1078 secinfo ("manifest", "Read content %s for %s", path, mPath.c_str ()); 1079 1080 // create a digest context 1081 CC_SHA1_CTX digestContext; 1082 CC_SHA1_Init (&digestContext); 1083 1084 // digest the data 1085 CC_SHA1_Update (&digestContext, path, result); 1086 1087 // compute the result 1088 CC_SHA1_Final (mDigest, &digestContext); 1089 1090 UnixError::check (result); 1091 } 1092 1093 1094 1095 const SHA1Digest* ManifestSymLinkItem::GetDigest () 1096 { 1097 return &mDigest; 1098 } 1099 1100 1101 1102 void ManifestSymLinkItem::SetDigest (const SHA1Digest* digest) 1103 { 1104 memcpy (mDigest, digest, sizeof (SHA1Digest)); 1105 } 1106 1107 1108 1109 ManifestItemType ManifestSymLinkItem::GetItemType () 1110 { 1111 return kManifestSymLinkItemType; 1112 } 1113 1114 1115 1116 void ManifestSymLinkItem::Compare (ManifestItem *a, bool compareOwnerAndGroup) 1117 { 1118 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1119 ManifestSymLinkItem* aa = static_cast<ManifestSymLinkItem*>(a); 1120 secinfo ("manifest", "Comparing symlink item %s against %s", GetName (), aa->GetName ()); 1121 1122 // now compare the data 1123 if (memcmp (&mDigest, &aa->mDigest, kSHA1DigestSize) != 0) 1124 { 1125 MacOSError::throwMe (errSecManifestNotEqual); 1126 } 1127 } 1128 1129 1130 1131 //========================== MANIFEST OTHER ITEM ========================== 1132 1133 1134 1135 ManifestOtherItem::ManifestOtherItem () 1136 { 1137 } 1138 1139 1140 1141 ManifestOtherItem::~ManifestOtherItem () 1142 { 1143 secinfo ("manifest", "Destroyed other item for path %s", mPath.c_str ()); 1144 } 1145 1146 1147 1148 ManifestItemType ManifestOtherItem::GetItemType () 1149 { 1150 return kManifestOtherType; 1151 } 1152 1153 1154 1155 void ManifestOtherItem::Compare (ManifestItem *a, bool compareOwnerAndGroup) 1156 { 1157 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1158 secinfo ("manifest", "Comparing other item %s against %s", GetName (), static_cast<FileSystemEntryItem*>(a)->GetName ()); 1159 }