@@ -594,82 +594,69 @@ public class CommonStat |
594 | 594 | private const char CanRead = 'r'; |
595 | 595 | private const char CanWrite = 'w'; |
596 | 596 | private const char CanExecute = 'x'; |
597 | | - |
598 | | - // helper for getting unix mode |
599 | | - private readonly Dictionary<StatMask, char> modeMap = new() |
600 | | - { |
601 | | - { StatMask.OwnerRead, CanRead }, |
602 | | - { StatMask.OwnerWrite, CanWrite }, |
603 | | - { StatMask.OwnerExecute, CanExecute }, |
604 | | - { StatMask.GroupRead, CanRead }, |
605 | | - { StatMask.GroupWrite, CanWrite }, |
606 | | - { StatMask.GroupExecute, CanExecute }, |
607 | | - { StatMask.OtherRead, CanRead }, |
608 | | - { StatMask.OtherWrite, CanWrite }, |
609 | | - { StatMask.OtherExecute, CanExecute }, |
610 | | - }; |
611 | | - |
612 | | - private readonly StatMask[] permissions = new StatMask[] |
613 | | - { |
614 | | - StatMask.OwnerRead, |
615 | | - StatMask.OwnerWrite, |
616 | | - StatMask.OwnerExecute, |
617 | | - StatMask.GroupRead, |
618 | | - StatMask.GroupWrite, |
619 | | - StatMask.GroupExecute, |
620 | | - StatMask.OtherRead, |
621 | | - StatMask.OtherWrite, |
622 | | - StatMask.OtherExecute |
623 | | - }; |
| 597 | + private const char NoPerm = '-'; |
| 598 | + private const char SetAndExec = 's'; |
| 599 | + private const char SetAndNotExec = 'S'; |
| 600 | + private const char StickyAndExec = 't'; |
| 601 | + private const char StickyAndNotExec = 'T'; |
624 | 602 |
|
625 | 603 | // The item type and the character representation for the first element in the stat string |
626 | | - private readonly Dictionary<ItemType, char> itemTypeTable = new() |
| 604 | + private static readonly Dictionary<ItemType, char> itemTypeTable = new() |
627 | 605 | { |
628 | | - { ItemType.BlockDevice, 'b' }, |
| 606 | + { ItemType.BlockDevice, 'b' }, |
629 | 607 | { ItemType.CharacterDevice, 'c' }, |
630 | | - { ItemType.Directory, 'd' }, |
631 | | - { ItemType.File, '-' }, |
632 | | - { ItemType.NamedPipe, 'p' }, |
633 | | - { ItemType.Socket, 's' }, |
634 | | - { ItemType.SymbolicLink, 'l' }, |
| 608 | + { ItemType.Directory, 'd' }, |
| 609 | + { ItemType.File, '-' }, |
| 610 | + { ItemType.NamedPipe, 'p' }, |
| 611 | + { ItemType.Socket, 's' }, |
| 612 | + { ItemType.SymbolicLink, 'l' }, |
635 | 613 | }; |
636 | 614 |
|
| 615 | + // We'll create a few common mode strings here to reduce allocations and improve performance a bit. |
| 616 | + private const string OwnerReadGroupReadOtherRead = "-r--r--r--"; |
| 617 | + private const string OwnerReadWriteGroupReadOtherRead = "-rw-r--r--"; |
| 618 | + private const string DirectoryOwnerFullGroupReadExecOtherReadExec = "drwxr-xr-x"; |
| 619 | + |
637 | 620 | /// <summary>Convert the mode to a string which is usable in our formatting.</summary> |
638 | 621 | /// <returns>The mode converted into a Unix style string similar to the output of ls.</returns> |
639 | 622 | public string GetModeString() |
640 | 623 | { |
641 | | - int offset = 0; |
642 | | - char[] modeCharacters = new char[10]; |
643 | | - modeCharacters[offset++] = itemTypeTable[ItemType]; |
| 624 | + // On an Ubuntu system (docker), these 3 are roughly 70% of all the permissions |
| 625 | + if ((Mode & 0xFFF) == 292) |
| 626 | + { |
| 627 | + return OwnerReadGroupReadOtherRead; |
| 628 | + } |
644 | 629 |
|
645 | | - foreach (StatMask permission in permissions) |
| 630 | + if ((Mode & 0xFFF) == 420) |
646 | 631 | { |
647 | | - // determine whether we are setuid, sticky, or the usual rwx. |
648 | | - if ((Mode & (int)permission) == (int)permission) |
649 | | - { |
650 | | - if ((permission == StatMask.OwnerExecute && IsSetUid) || (permission == StatMask.GroupExecute && IsSetGid)) |
651 | | - { |
652 | | - // Check for setuid and add 's' |
653 | | - modeCharacters[offset] = 's'; |
654 | | - } |
655 | | - else if (permission == StatMask.OtherExecute && IsSticky && (ItemType == ItemType.Directory)) |
656 | | - { |
657 | | - // Directories are sticky, rather than setuid |
658 | | - modeCharacters[offset] = 't'; |
659 | | - } |
660 | | - else |
661 | | - { |
662 | | - modeCharacters[offset] = modeMap[permission]; |
663 | | - } |
664 | | - } |
665 | | - else |
666 | | - { |
667 | | - modeCharacters[offset] = '-'; |
668 | | - } |
| 632 | + return OwnerReadWriteGroupReadOtherRead; |
| 633 | + } |
669 | 634 |
|
670 | | - offset++; |
| 635 | + if (ItemType == ItemType.Directory & (Mode & 0xFFF) == 493) |
| 636 | + { |
| 637 | + return DirectoryOwnerFullGroupReadExecOtherReadExec; |
671 | 638 | } |
672 | 639 |
|
| 640 | + Span<char> modeCharacters = stackalloc char[10]; |
| 641 | + modeCharacters[0] = itemTypeTable[ItemType]; |
| 642 | + bool isExecutable; |
| 643 | + |
| 644 | + UnixFileMode modeInfo = (UnixFileMode)Mode; |
| 645 | + modeCharacters[1] = modeInfo.HasFlag(UnixFileMode.UserRead) ? CanRead : NoPerm; |
| 646 | + modeCharacters[2] = modeInfo.HasFlag(UnixFileMode.UserWrite) ? CanWrite : NoPerm; |
| 647 | + isExecutable = modeInfo.HasFlag(UnixFileMode.UserExecute); |
| 648 | + modeCharacters[3] = modeInfo.HasFlag(UnixFileMode.SetUser) ? (isExecutable ? SetAndExec : SetAndNotExec) : (isExecutable ? CanExecute : NoPerm); |
| 649 | + |
| 650 | + modeCharacters[4] = modeInfo.HasFlag(UnixFileMode.GroupRead) ? CanRead : NoPerm; |
| 651 | + modeCharacters[5] = modeInfo.HasFlag(UnixFileMode.GroupWrite) ? CanWrite : NoPerm; |
| 652 | + isExecutable = modeInfo.HasFlag(UnixFileMode.GroupExecute); |
| 653 | + modeCharacters[6] = modeInfo.HasFlag(UnixFileMode.SetGroup) ? (isExecutable ? SetAndExec : SetAndNotExec) : (isExecutable ? CanExecute : NoPerm); |
| 654 | + |
| 655 | + modeCharacters[7] = modeInfo.HasFlag(UnixFileMode.OtherRead) ? CanRead : NoPerm; |
| 656 | + modeCharacters[8] = modeInfo.HasFlag(UnixFileMode.OtherWrite) ? CanWrite : NoPerm; |
| 657 | + isExecutable = modeInfo.HasFlag(UnixFileMode.OtherExecute); |
| 658 | + modeCharacters[9] = modeInfo.HasFlag(UnixFileMode.StickyBit) ? (isExecutable ? StickyAndExec : StickyAndNotExec) : (isExecutable ? CanExecute : NoPerm); |
| 659 | + |
673 | 660 | return new string(modeCharacters); |
674 | 661 | } |
675 | 662 |
|
|