1// PropIDUtils.cpp 2 3#include "StdAfx.h" 4 5#include "../../../../C/CpuArch.h" 6 7#include "../../../Common/IntToString.h" 8#include "../../../Common/StringConvert.h" 9 10#include "../../../Windows/FileIO.h" 11#include "../../../Windows/PropVariantConv.h" 12 13#include "../../PropID.h" 14 15#include "PropIDUtils.h" 16 17#ifndef Z7_SFX 18#define Get16(x) GetUi16(x) 19#define Get32(x) GetUi32(x) 20#endif 21 22using namespace NWindows; 23 24static const unsigned kNumWinAtrribFlags = 21; 25static const char g_WinAttribChars[kNumWinAtrribFlags + 1] = "RHS8DAdNTsLCOIEV.X.PU"; 26 27/* 28FILE_ATTRIBUTE_ 29 300 READONLY 311 HIDDEN 322 SYSTEM 333 (Volume label - obsolete) 344 DIRECTORY 355 ARCHIVE 366 DEVICE 377 NORMAL 388 TEMPORARY 399 SPARSE_FILE 4010 REPARSE_POINT 4111 COMPRESSED 4212 OFFLINE 4313 NOT_CONTENT_INDEXED (I - Win10 attrib/Explorer) 4414 ENCRYPTED 4515 INTEGRITY_STREAM (V - ReFS Win8/Win2012) 4616 VIRTUAL (reserved) 4717 NO_SCRUB_DATA (X - ReFS Win8/Win2012 attrib) 4818 RECALL_ON_OPEN or EA 4919 PINNED 5020 UNPINNED 5121 STRICTLY_SEQUENTIAL 5222 RECALL_ON_DATA_ACCESS 53*/ 54 55 56static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' }; 57#define MY_ATTR_CHAR(a, n, c) (((a) & (1 << (n))) ? c : '-') 58 59static void ConvertPosixAttribToString(char *s, UInt32 a) throw() 60{ 61 s[0] = kPosixTypes[(a >> 12) & 0xF]; 62 for (int i = 6; i >= 0; i -= 3) 63 { 64 s[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r'); 65 s[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w'); 66 s[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x'); 67 } 68 if ((a & 0x800) != 0) s[3] = ((a & (1 << 6)) ? 's' : 'S'); // S_ISUID 69 if ((a & 0x400) != 0) s[6] = ((a & (1 << 3)) ? 's' : 'S'); // S_ISGID 70 if ((a & 0x200) != 0) s[9] = ((a & (1 << 0)) ? 't' : 'T'); // S_ISVTX 71 s[10] = 0; 72 73 a &= ~(UInt32)0xFFFF; 74 if (a != 0) 75 { 76 s[10] = ' '; 77 ConvertUInt32ToHex8Digits(a, s + 11); 78 } 79} 80 81 82void ConvertWinAttribToString(char *s, UInt32 wa) throw() 83{ 84 /* 85 some programs store posix attributes in high 16 bits. 86 p7zip - stores additional 0x8000 flag marker. 87 macos - stores additional 0x4000 flag marker. 88 info-zip - no additional marker. 89 */ 90 91 const bool isPosix = ((wa & 0xF0000000) != 0); 92 93 UInt32 posix = 0; 94 if (isPosix) 95 { 96 posix = wa >> 16; 97 wa &= (UInt32)0x3FFF; 98 } 99 100 for (unsigned i = 0; i < kNumWinAtrribFlags; i++) 101 { 102 UInt32 flag = (1 << i); 103 if ((wa & flag) != 0) 104 { 105 char c = g_WinAttribChars[i]; 106 if (c != '.') 107 { 108 wa &= ~flag; 109 // if (i != 7) // we can disable N (NORMAL) printing 110 *s++ = c; 111 } 112 } 113 } 114 115 if (wa != 0) 116 { 117 *s++ = ' '; 118 ConvertUInt32ToHex8Digits(wa, s); 119 s += strlen(s); 120 } 121 122 *s = 0; 123 124 if (isPosix) 125 { 126 *s++ = ' '; 127 ConvertPosixAttribToString(s, posix); 128 } 129} 130 131 132void ConvertPropertyToShortString2(char *dest, const PROPVARIANT &prop, PROPID propID, int level) throw() 133{ 134 *dest = 0; 135 136 if (prop.vt == VT_FILETIME) 137 { 138 const FILETIME &ft = prop.filetime; 139 unsigned ns100 = 0; 140 int numDigits = kTimestampPrintLevel_NTFS; 141 const unsigned prec = prop.wReserved1; 142 const unsigned ns100_Temp = prop.wReserved2; 143 if (prec != 0 144 && prec <= k_PropVar_TimePrec_1ns 145 && ns100_Temp < 100 146 && prop.wReserved3 == 0) 147 { 148 ns100 = ns100_Temp; 149 if (prec == k_PropVar_TimePrec_Unix || 150 prec == k_PropVar_TimePrec_DOS) 151 numDigits = 0; 152 else if (prec == k_PropVar_TimePrec_HighPrec) 153 numDigits = 9; 154 else 155 { 156 numDigits = (int)prec - (int)k_PropVar_TimePrec_Base; 157 if ( 158 // numDigits < kTimestampPrintLevel_DAY // for debuf 159 numDigits < kTimestampPrintLevel_SEC 160 ) 161 162 numDigits = kTimestampPrintLevel_NTFS; 163 } 164 } 165 if (ft.dwHighDateTime == 0 && ft.dwLowDateTime == 0 && ns100 == 0) 166 return; 167 if (level > numDigits) 168 level = numDigits; 169 ConvertUtcFileTimeToString2(ft, ns100, dest, level); 170 return; 171 } 172 173 switch (propID) 174 { 175 case kpidCRC: 176 { 177 if (prop.vt != VT_UI4) 178 break; 179 ConvertUInt32ToHex8Digits(prop.ulVal, dest); 180 return; 181 } 182 case kpidAttrib: 183 { 184 if (prop.vt != VT_UI4) 185 break; 186 const UInt32 a = prop.ulVal; 187 188 /* 189 if ((a & 0x8000) && (a & 0x7FFF) == 0) 190 ConvertPosixAttribToString(dest, a >> 16); 191 else 192 */ 193 ConvertWinAttribToString(dest, a); 194 return; 195 } 196 case kpidPosixAttrib: 197 { 198 if (prop.vt != VT_UI4) 199 break; 200 ConvertPosixAttribToString(dest, prop.ulVal); 201 return; 202 } 203 case kpidINode: 204 { 205 if (prop.vt != VT_UI8) 206 break; 207 ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest); 208 dest += strlen(dest); 209 *dest++ = '-'; 210 const UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1); 211 ConvertUInt64ToString(low, dest); 212 return; 213 } 214 case kpidVa: 215 { 216 UInt64 v = 0; 217 if (prop.vt == VT_UI4) 218 v = prop.ulVal; 219 else if (prop.vt == VT_UI8) 220 v = (UInt64)prop.uhVal.QuadPart; 221 else 222 break; 223 dest[0] = '0'; 224 dest[1] = 'x'; 225 ConvertUInt64ToHex(v, dest + 2); 226 return; 227 } 228 229 /* 230 case kpidDevice: 231 { 232 UInt64 v = 0; 233 if (prop.vt == VT_UI4) 234 v = prop.ulVal; 235 else if (prop.vt == VT_UI8) 236 v = (UInt64)prop.uhVal.QuadPart; 237 else 238 break; 239 ConvertUInt32ToString(MY_dev_major(v), dest); 240 dest += strlen(dest); 241 *dest++ = ','; 242 ConvertUInt32ToString(MY_dev_minor(v), dest); 243 return; 244 } 245 */ 246 } 247 248 ConvertPropVariantToShortString(prop, dest); 249} 250 251void ConvertPropertyToString2(UString &dest, const PROPVARIANT &prop, PROPID propID, int level) 252{ 253 if (prop.vt == VT_BSTR) 254 { 255 dest.SetFromBstr(prop.bstrVal); 256 return; 257 } 258 char temp[64]; 259 ConvertPropertyToShortString2(temp, prop, propID, level); 260 dest = temp; 261} 262 263#ifndef Z7_SFX 264 265static inline unsigned GetHex(unsigned v) 266{ 267 return (v < 10) ? ('0' + v) : ('A' + (v - 10)); 268} 269 270static inline void AddHexToString(AString &res, unsigned v) 271{ 272 res += (char)GetHex(v >> 4); 273 res += (char)GetHex(v & 0xF); 274} 275 276/* 277static AString Data_To_Hex(const Byte *data, size_t size) 278{ 279 AString s; 280 for (size_t i = 0; i < size; i++) 281 AddHexToString(s, data[i]); 282 return s; 283} 284*/ 285 286static const char * const sidNames[] = 287{ 288 "0" 289 , "Dialup" 290 , "Network" 291 , "Batch" 292 , "Interactive" 293 , "Logon" // S-1-5-5-X-Y 294 , "Service" 295 , "Anonymous" 296 , "Proxy" 297 , "EnterpriseDC" 298 , "Self" 299 , "AuthenticatedUsers" 300 , "RestrictedCode" 301 , "TerminalServer" 302 , "RemoteInteractiveLogon" 303 , "ThisOrganization" 304 , "16" 305 , "IUserIIS" 306 , "LocalSystem" 307 , "LocalService" 308 , "NetworkService" 309 , "Domains" 310}; 311 312struct CSecID2Name 313{ 314 UInt32 n; 315 const char *sz; 316}; 317 318static int FindPairIndex(const CSecID2Name * pairs, unsigned num, UInt32 id) 319{ 320 for (unsigned i = 0; i < num; i++) 321 if (pairs[i].n == id) 322 return (int)i; 323 return -1; 324} 325 326static const CSecID2Name sid_32_Names[] = 327{ 328 { 544, "Administrators" }, 329 { 545, "Users" }, 330 { 546, "Guests" }, 331 { 547, "PowerUsers" }, 332 { 548, "AccountOperators" }, 333 { 549, "ServerOperators" }, 334 { 550, "PrintOperators" }, 335 { 551, "BackupOperators" }, 336 { 552, "Replicators" }, 337 { 553, "Backup Operators" }, 338 { 554, "PreWindows2000CompatibleAccess" }, 339 { 555, "RemoteDesktopUsers" }, 340 { 556, "NetworkConfigurationOperators" }, 341 { 557, "IncomingForestTrustBuilders" }, 342 { 558, "PerformanceMonitorUsers" }, 343 { 559, "PerformanceLogUsers" }, 344 { 560, "WindowsAuthorizationAccessGroup" }, 345 { 561, "TerminalServerLicenseServers" }, 346 { 562, "DistributedCOMUsers" }, 347 { 569, "CryptographicOperators" }, 348 { 573, "EventLogReaders" }, 349 { 574, "CertificateServiceDCOMAccess" } 350}; 351 352static const CSecID2Name sid_21_Names[] = 353{ 354 { 500, "Administrator" }, 355 { 501, "Guest" }, 356 { 502, "KRBTGT" }, 357 { 512, "DomainAdmins" }, 358 { 513, "DomainUsers" }, 359 { 515, "DomainComputers" }, 360 { 516, "DomainControllers" }, 361 { 517, "CertPublishers" }, 362 { 518, "SchemaAdmins" }, 363 { 519, "EnterpriseAdmins" }, 364 { 520, "GroupPolicyCreatorOwners" }, 365 { 553, "RASandIASServers" }, 366 { 553, "RASandIASServers" }, 367 { 571, "AllowedRODCPasswordReplicationGroup" }, 368 { 572, "DeniedRODCPasswordReplicationGroup" } 369}; 370 371struct CServicesToName 372{ 373 UInt32 n[5]; 374 const char *sz; 375}; 376 377static const CServicesToName services_to_name[] = 378{ 379 { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" } 380}; 381 382static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize) 383{ 384 sidSize = 0; 385 if (lim < 8) 386 { 387 s += "ERROR"; 388 return; 389 } 390 const UInt32 rev = p[0]; 391 if (rev != 1) 392 { 393 s += "UNSUPPORTED"; 394 return; 395 } 396 const UInt32 num = p[1]; 397 if (8 + num * 4 > lim) 398 { 399 s += "ERROR"; 400 return; 401 } 402 sidSize = 8 + num * 4; 403 const UInt32 authority = GetBe32(p + 4); 404 405 if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1) 406 { 407 const UInt32 v0 = Get32(p + 8); 408 if (v0 < Z7_ARRAY_SIZE(sidNames)) 409 { 410 s += sidNames[v0]; 411 return; 412 } 413 if (v0 == 32 && num == 2) 414 { 415 const UInt32 v1 = Get32(p + 12); 416 const int index = FindPairIndex(sid_32_Names, Z7_ARRAY_SIZE(sid_32_Names), v1); 417 if (index >= 0) 418 { 419 s += sid_32_Names[(unsigned)index].sz; 420 return; 421 } 422 } 423 if (v0 == 21 && num == 5) 424 { 425 UInt32 v4 = Get32(p + 8 + 4 * 4); 426 const int index = FindPairIndex(sid_21_Names, Z7_ARRAY_SIZE(sid_21_Names), v4); 427 if (index >= 0) 428 { 429 s += sid_21_Names[(unsigned)index].sz; 430 return; 431 } 432 } 433 if (v0 == 80 && num == 6) 434 { 435 for (unsigned i = 0; i < Z7_ARRAY_SIZE(services_to_name); i++) 436 { 437 const CServicesToName &sn = services_to_name[i]; 438 int j; 439 for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++); 440 if (j == 5) 441 { 442 s += sn.sz; 443 return; 444 } 445 } 446 } 447 } 448 449 s += "S-1-"; 450 if (p[2] == 0 && p[3] == 0) 451 s.Add_UInt32(authority); 452 else 453 { 454 s += "0x"; 455 for (int i = 2; i < 8; i++) 456 AddHexToString(s, p[i]); 457 } 458 for (UInt32 i = 0; i < num; i++) 459 { 460 s.Add_Minus(); 461 s.Add_UInt32(Get32(p + 8 + i * 4)); 462 } 463} 464 465static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos) 466{ 467 if (pos > size) 468 { 469 s += "ERROR"; 470 return; 471 } 472 UInt32 sidSize = 0; 473 ParseSid(s, p + pos, size - pos, sidSize); 474} 475 476static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset) 477{ 478 const UInt32 control = Get16(p + 2); 479 if ((flags & control) == 0) 480 return; 481 const UInt32 pos = Get32(p + offset); 482 s.Add_Space(); 483 s += strName; 484 if (pos >= size) 485 return; 486 p += pos; 487 size -= pos; 488 if (size < 8) 489 return; 490 if (Get16(p) != 2) // revision 491 return; 492 const UInt32 num = Get32(p + 4); 493 s.Add_UInt32(num); 494 495 /* 496 UInt32 aclSize = Get16(p + 2); 497 if (num >= (1 << 16)) 498 return; 499 if (aclSize > size) 500 return; 501 size = aclSize; 502 size -= 8; 503 p += 8; 504 for (UInt32 i = 0 ; i < num; i++) 505 { 506 if (size <= 8) 507 return; 508 // Byte type = p[0]; 509 // Byte flags = p[1]; 510 // UInt32 aceSize = Get16(p + 2); 511 // UInt32 mask = Get32(p + 4); 512 p += 8; 513 size -= 8; 514 515 UInt32 sidSize = 0; 516 s.Add_Space(); 517 ParseSid(s, p, size, sidSize); 518 if (sidSize == 0) 519 return; 520 p += sidSize; 521 size -= sidSize; 522 } 523 524 // the tail can contain zeros. So (size != 0) is not ERROR 525 // if (size != 0) s += " ERROR"; 526 */ 527} 528 529/* 530#define MY_SE_OWNER_DEFAULTED (0x0001) 531#define MY_SE_GROUP_DEFAULTED (0x0002) 532*/ 533#define MY_SE_DACL_PRESENT (0x0004) 534/* 535#define MY_SE_DACL_DEFAULTED (0x0008) 536*/ 537#define MY_SE_SACL_PRESENT (0x0010) 538/* 539#define MY_SE_SACL_DEFAULTED (0x0020) 540#define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100) 541#define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200) 542#define MY_SE_DACL_AUTO_INHERITED (0x0400) 543#define MY_SE_SACL_AUTO_INHERITED (0x0800) 544#define MY_SE_DACL_PROTECTED (0x1000) 545#define MY_SE_SACL_PROTECTED (0x2000) 546#define MY_SE_RM_CONTROL_VALID (0x4000) 547#define MY_SE_SELF_RELATIVE (0x8000) 548*/ 549 550void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s) 551{ 552 s.Empty(); 553 if (size < 20 || size > (1 << 18)) 554 { 555 s += "ERROR"; 556 return; 557 } 558 if (Get16(data) != 1) // revision 559 { 560 s += "UNSUPPORTED"; 561 return; 562 } 563 ParseOwner(s, data, size, Get32(data + 4)); 564 s.Add_Space(); 565 ParseOwner(s, data, size, Get32(data + 8)); 566 ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12); 567 ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16); 568 s.Add_Space(); 569 s.Add_UInt32(size); 570 // s += '\n'; 571 // s += Data_To_Hex(data, size); 572} 573 574#ifdef _WIN32 575 576static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) throw() 577{ 578 if (pos >= size) 579 return false; 580 size -= pos; 581 if (size < 8) 582 return false; 583 const UInt32 rev = data[pos]; 584 if (rev != 1) 585 return false; 586 const UInt32 num = data[pos + 1]; 587 return (8 + num * 4 <= size); 588} 589 590static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) throw() 591{ 592 const UInt32 control = Get16(p + 2); 593 if ((flags & control) == 0) 594 return true; 595 const UInt32 pos = Get32(p + offset); 596 if (pos >= size) 597 return false; 598 p += pos; 599 size -= pos; 600 if (size < 8) 601 return false; 602 const UInt32 aclSize = Get16(p + 2); 603 return (aclSize <= size); 604} 605 606bool CheckNtSecure(const Byte *data, UInt32 size) throw() 607{ 608 if (size < 20) 609 return false; 610 if (Get16(data) != 1) // revision 611 return true; // windows function can handle such error, so we allow it 612 if (size > (1 << 18)) 613 return false; 614 if (!CheckSid(data, size, Get32(data + 4))) return false; 615 if (!CheckSid(data, size, Get32(data + 8))) return false; 616 if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false; 617 if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false; 618 return true; 619} 620 621#endif 622 623 624 625// IO_REPARSE_TAG_* 626 627static const CSecID2Name k_ReparseTags[] = 628{ 629 { 0xA0000003, "MOUNT_POINT" }, 630 { 0xC0000004, "HSM" }, 631 { 0x80000005, "DRIVE_EXTENDER" }, 632 { 0x80000006, "HSM2" }, 633 { 0x80000007, "SIS" }, 634 { 0x80000008, "WIM" }, 635 { 0x80000009, "CSV" }, 636 { 0x8000000A, "DFS" }, 637 { 0x8000000B, "FILTER_MANAGER" }, 638 { 0xA000000C, "SYMLINK" }, 639 { 0xA0000010, "IIS_CACHE" }, 640 { 0x80000012, "DFSR" }, 641 { 0x80000013, "DEDUP" }, 642 { 0xC0000014, "APPXSTRM" }, 643 { 0x80000014, "NFS" }, 644 { 0x80000015, "FILE_PLACEHOLDER" }, 645 { 0x80000016, "DFM" }, 646 { 0x80000017, "WOF" }, 647 { 0x80000018, "WCI" }, 648 { 0x8000001B, "APPEXECLINK" }, 649 { 0xA000001D, "LX_SYMLINK" }, 650 { 0x80000023, "AF_UNIX" }, 651 { 0x80000024, "LX_FIFO" }, 652 { 0x80000025, "LX_CHR" }, 653 { 0x80000026, "LX_BLK" } 654}; 655 656bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s) 657{ 658 s.Empty(); 659 NFile::CReparseAttr attr; 660 661 if (attr.Parse(data, size)) 662 { 663 if (attr.IsSymLink_WSL()) 664 { 665 s += "WSL: "; 666 s += attr.GetPath(); 667 } 668 else 669 { 670 if (!attr.IsSymLink_Win()) 671 s += "Junction: "; 672 s += attr.GetPath(); 673 if (s.IsEmpty()) 674 s += "Link: "; 675 if (!attr.IsOkNamePair()) 676 { 677 s += " : "; 678 s += attr.PrintName; 679 } 680 } 681 if (attr.MinorError) 682 s += " : MINOR_ERROR"; 683 return true; 684 // s += " "; // for debug 685 } 686 687 if (size < 8) 688 return false; 689 const UInt32 tag = Get32(data); 690 const UInt32 len = Get16(data + 4); 691 if (len + 8 > size) 692 return false; 693 if (Get16(data + 6) != 0) // padding 694 return false; 695 696 /* 697 #define my_IO_REPARSE_TAG_DEDUP (0x80000013L) 698 if (tag == my_IO_REPARSE_TAG_DEDUP) 699 { 700 } 701 */ 702 703 { 704 const int index = FindPairIndex(k_ReparseTags, Z7_ARRAY_SIZE(k_ReparseTags), tag); 705 if (index >= 0) 706 s += k_ReparseTags[(unsigned)index].sz; 707 else 708 { 709 s += "REPARSE:"; 710 char hex[16]; 711 ConvertUInt32ToHex8Digits(tag, hex); 712 s += hex; 713 } 714 } 715 716 s += ":"; 717 s.Add_UInt32(len); 718 719 if (len != 0) 720 { 721 s.Add_Space(); 722 723 data += 8; 724 725 for (UInt32 i = 0; i < len; i++) 726 { 727 if (i >= 16) 728 { 729 s += "..."; 730 break; 731 } 732 const unsigned b = data[i]; 733 s += (char)GetHex((b >> 4) & 0xF); 734 s += (char)GetHex(b & 0xF); 735 } 736 } 737 738 return true; 739} 740 741#endif 742