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
22 using namespace NWindows;
23
24 static const unsigned kNumWinAtrribFlags = 21;
25 static const char g_WinAttribChars[kNumWinAtrribFlags + 1] = "RHS8DAdNTsLCOIEV.X.PU";
26
27 /*
28 FILE_ATTRIBUTE_
29
30 0 READONLY
31 1 HIDDEN
32 2 SYSTEM
33 3 (Volume label - obsolete)
34 4 DIRECTORY
35 5 ARCHIVE
36 6 DEVICE
37 7 NORMAL
38 8 TEMPORARY
39 9 SPARSE_FILE
40 10 REPARSE_POINT
41 11 COMPRESSED
42 12 OFFLINE
43 13 NOT_CONTENT_INDEXED (I - Win10 attrib/Explorer)
44 14 ENCRYPTED
45 15 INTEGRITY_STREAM (V - ReFS Win8/Win2012)
46 16 VIRTUAL (reserved)
47 17 NO_SCRUB_DATA (X - ReFS Win8/Win2012 attrib)
48 18 RECALL_ON_OPEN or EA
49 19 PINNED
50 20 UNPINNED
51 21 STRICTLY_SEQUENTIAL
52 22 RECALL_ON_DATA_ACCESS
53 */
54
55
56 static 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
ConvertPosixAttribToString(char * s,UInt32 a)59 static 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
ConvertWinAttribToString(char * s,UInt32 wa)82 void 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
ConvertPropertyToShortString2(char * dest,const PROPVARIANT & prop,PROPID propID,int level)132 void 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
ConvertPropertyToString2(UString & dest,const PROPVARIANT & prop,PROPID propID,int level)251 void 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
GetHex(unsigned v)265 static inline unsigned GetHex(unsigned v)
266 {
267 return (v < 10) ? ('0' + v) : ('A' + (v - 10));
268 }
269
AddHexToString(AString & res,unsigned v)270 static inline void AddHexToString(AString &res, unsigned v)
271 {
272 res += (char)GetHex(v >> 4);
273 res += (char)GetHex(v & 0xF);
274 }
275
276 /*
277 static 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
286 static 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
312 struct CSecID2Name
313 {
314 UInt32 n;
315 const char *sz;
316 };
317
FindPairIndex(const CSecID2Name * pairs,unsigned num,UInt32 id)318 static 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
326 static 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
352 static 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
371 struct CServicesToName
372 {
373 UInt32 n[5];
374 const char *sz;
375 };
376
377 static const CServicesToName services_to_name[] =
378 {
379 { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" }
380 };
381
ParseSid(AString & s,const Byte * p,UInt32 lim,UInt32 & sidSize)382 static 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
ParseOwner(AString & s,const Byte * p,UInt32 size,UInt32 pos)465 static 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
ParseAcl(AString & s,const Byte * p,UInt32 size,const char * strName,UInt32 flags,UInt32 offset)476 static 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
ConvertNtSecureToString(const Byte * data,UInt32 size,AString & s)550 void 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
CheckSid(const Byte * data,UInt32 size,UInt32 pos)576 static 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
CheckAcl(const Byte * p,UInt32 size,UInt32 flags,UInt32 offset)590 static 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
CheckNtSecure(const Byte * data,UInt32 size)606 bool 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
627 static 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
ConvertNtReparseToString(const Byte * data,UInt32 size,UString & s)656 bool 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