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