GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix `unixmode` to handle `setuid` and `sticky` when file is not an ex… · PowerShell/PowerShell@81a7f6f

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • src/System.Management.Automation/CoreCLR
    • CorePsPlatform.cs
  • test/powershell/Modules/Microsoft.PowerShell.Management
    • UnixStat.Tests.ps1
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/System.Management.Automation/CoreCLR/CorePsPlatform.cs‎

Copy file name to clipboardExpand all lines: src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
+48-61Lines changed: 48 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -594,82 +594,69 @@ public class CommonStat
594594
private const char CanRead = 'r';
595595
private const char CanWrite = 'w';
596596
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';
624602

625603
// 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()
627605
{
628-
{ ItemType.BlockDevice, 'b' },
606+
{ ItemType.BlockDevice, 'b' },
629607
{ 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' },
635613
};
636614

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+
637620
/// <summary>Convert the mode to a string which is usable in our formatting.</summary>
638621
/// <returns>The mode converted into a Unix style string similar to the output of ls.</returns>
639622
public string GetModeString()
640623
{
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+
}
644629

645-
foreach (StatMask permission in permissions)
630+
if ((Mode & 0xFFF) == 420)
646631
{
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+
}
669634

670-
offset++;
635+
if (ItemType == ItemType.Directory & (Mode & 0xFFF) == 493)
636+
{
637+
return DirectoryOwnerFullGroupReadExecOtherReadExec;
671638
}
672639

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+
673660
return new string(modeCharacters);
674661
}
675662

Collapse file

‎test/powershell/Modules/Microsoft.PowerShell.Management/UnixStat.Tests.ps1‎

Copy file name to clipboardExpand all lines: test/powershell/Modules/Microsoft.PowerShell.Management/UnixStat.Tests.ps1
+13-2Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ Describe "UnixFileSystem additions" -Tag "CI" {
3737
@{ Mode = '555'; Perm = '-r-xr-xr-x'; Item = "${testFile}" },
3838
@{ Mode = '666'; Perm = '-rw-rw-rw-'; Item = "${testFile}" },
3939
@{ Mode = '777'; Perm = '-rwxrwxrwx'; Item = "${testFile}" },
40+
@{ Mode = '4644'; Perm = '-rwSr--r--'; Item = "${testFile}" },
41+
@{ Mode = '1644'; Perm = '-rw-r--r-T'; Item = "${testFile}" },
42+
@{ Mode = '2644'; Perm = '-rw-r-Sr--'; Item = "${testFile}" },
43+
@{ Mode = '7644'; Perm = '-rwSr-Sr-T'; Item = "${testFile}" },
4044
@{ Mode = '4777'; Perm = '-rwsrwxrwx'; Item = "${testFile}" },
4145
@{ Mode = '1777'; Perm = 'drwxrwxrwt'; Item = "${testDir}" }
4246
}
@@ -57,9 +61,16 @@ Describe "UnixFileSystem additions" -Tag "CI" {
5761

5862
It "Should present filemode '<Mode>' string correctly as '<Perm>'" -testCase $testCase {
5963
param ($Mode, $Perm, $Item )
64+
# chmod can fail for some modes so be sure to handle that here.
65+
# specifically, when setting setgid chmod can fail if the group is privileged.
6066
chmod "$Mode" "${Item}"
61-
$i = Get-Item $Item
62-
$i.UnixMode | Should -Be $Perm
67+
if ($LASTEXITCODE -ne 0) {
68+
set-itresult -skip -because "chmod '$mode' failed"
69+
}
70+
else {
71+
$i = Get-Item $Item
72+
$i.UnixMode | Should -BeExactly $Perm
73+
}
6374
}
6475

6576
It "Should retrieve the user name for the file" {

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.