Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 48 additions & 61 deletions src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -594,82 +594,69 @@ public class CommonStat
private const char CanRead = 'r';
private const char CanWrite = 'w';
private const char CanExecute = 'x';

// helper for getting unix mode
private readonly Dictionary<StatMask, char> modeMap = new()
{
{ StatMask.OwnerRead, CanRead },
{ StatMask.OwnerWrite, CanWrite },
{ StatMask.OwnerExecute, CanExecute },
{ StatMask.GroupRead, CanRead },
{ StatMask.GroupWrite, CanWrite },
{ StatMask.GroupExecute, CanExecute },
{ StatMask.OtherRead, CanRead },
{ StatMask.OtherWrite, CanWrite },
{ StatMask.OtherExecute, CanExecute },
};

private readonly StatMask[] permissions = new StatMask[]
{
StatMask.OwnerRead,
StatMask.OwnerWrite,
StatMask.OwnerExecute,
StatMask.GroupRead,
StatMask.GroupWrite,
StatMask.GroupExecute,
StatMask.OtherRead,
StatMask.OtherWrite,
StatMask.OtherExecute
};
private const char NoPerm = '-';
private const char SetAndExec = 's';
private const char SetAndNotExec = 'S';
private const char StickyAndExec = 't';
private const char StickyAndNotExec = 'T';

// The item type and the character representation for the first element in the stat string
private readonly Dictionary<ItemType, char> itemTypeTable = new()
private static readonly Dictionary<ItemType, char> itemTypeTable = new()
{
{ ItemType.BlockDevice, 'b' },
{ ItemType.BlockDevice, 'b' },
{ ItemType.CharacterDevice, 'c' },
{ ItemType.Directory, 'd' },
{ ItemType.File, '-' },
{ ItemType.NamedPipe, 'p' },
{ ItemType.Socket, 's' },
{ ItemType.SymbolicLink, 'l' },
{ ItemType.Directory, 'd' },
{ ItemType.File, '-' },
{ ItemType.NamedPipe, 'p' },
{ ItemType.Socket, 's' },
{ ItemType.SymbolicLink, 'l' },
};

// We'll create a few common mode strings here to reduce allocations and improve performance a bit.
private const string OwnerReadGroupReadOtherRead = "-r--r--r--";
private const string OwnerReadWriteGroupReadOtherRead = "-rw-r--r--";
private const string DirectoryOwnerFullGroupReadExecOtherReadExec = "drwxr-xr-x";

/// <summary>Convert the mode to a string which is usable in our formatting.</summary>
/// <returns>The mode converted into a Unix style string similar to the output of ls.</returns>
public string GetModeString()
{
int offset = 0;
char[] modeCharacters = new char[10];
modeCharacters[offset++] = itemTypeTable[ItemType];
// On an Ubuntu system (docker), these 3 are roughly 70% of all the permissions
if ((Mode & 0xFFF) == 292)
{
return OwnerReadGroupReadOtherRead;
}

foreach (StatMask permission in permissions)
if ((Mode & 0xFFF) == 420)
{
// determine whether we are setuid, sticky, or the usual rwx.
if ((Mode & (int)permission) == (int)permission)
{
if ((permission == StatMask.OwnerExecute && IsSetUid) || (permission == StatMask.GroupExecute && IsSetGid))
{
// Check for setuid and add 's'
modeCharacters[offset] = 's';
}
else if (permission == StatMask.OtherExecute && IsSticky && (ItemType == ItemType.Directory))
{
// Directories are sticky, rather than setuid
modeCharacters[offset] = 't';
}
else
{
modeCharacters[offset] = modeMap[permission];
}
}
else
{
modeCharacters[offset] = '-';
}
return OwnerReadWriteGroupReadOtherRead;
}

offset++;
if (ItemType == ItemType.Directory & (Mode & 0xFFF) == 493)
{
return DirectoryOwnerFullGroupReadExecOtherReadExec;
}

Span<char> modeCharacters = stackalloc char[10];
modeCharacters[0] = itemTypeTable[ItemType];
bool isExecutable;

UnixFileMode modeInfo = (UnixFileMode)Mode;
modeCharacters[1] = modeInfo.HasFlag(UnixFileMode.UserRead) ? CanRead : NoPerm;
modeCharacters[2] = modeInfo.HasFlag(UnixFileMode.UserWrite) ? CanWrite : NoPerm;
isExecutable = modeInfo.HasFlag(UnixFileMode.UserExecute);
modeCharacters[3] = modeInfo.HasFlag(UnixFileMode.SetUser) ? (isExecutable ? SetAndExec : SetAndNotExec) : (isExecutable ? CanExecute : NoPerm);

modeCharacters[4] = modeInfo.HasFlag(UnixFileMode.GroupRead) ? CanRead : NoPerm;
modeCharacters[5] = modeInfo.HasFlag(UnixFileMode.GroupWrite) ? CanWrite : NoPerm;
isExecutable = modeInfo.HasFlag(UnixFileMode.GroupExecute);
modeCharacters[6] = modeInfo.HasFlag(UnixFileMode.SetGroup) ? (isExecutable ? SetAndExec : SetAndNotExec) : (isExecutable ? CanExecute : NoPerm);

modeCharacters[7] = modeInfo.HasFlag(UnixFileMode.OtherRead) ? CanRead : NoPerm;
modeCharacters[8] = modeInfo.HasFlag(UnixFileMode.OtherWrite) ? CanWrite : NoPerm;
isExecutable = modeInfo.HasFlag(UnixFileMode.OtherExecute);
modeCharacters[9] = modeInfo.HasFlag(UnixFileMode.StickyBit) ? (isExecutable ? StickyAndExec : StickyAndNotExec) : (isExecutable ? CanExecute : NoPerm);

return new string(modeCharacters);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Describe "UnixFileSystem additions" -Tag "CI" {
@{ Mode = '555'; Perm = '-r-xr-xr-x'; Item = "${testFile}" },
@{ Mode = '666'; Perm = '-rw-rw-rw-'; Item = "${testFile}" },
@{ Mode = '777'; Perm = '-rwxrwxrwx'; Item = "${testFile}" },
@{ Mode = '4644'; Perm = '-rwSr--r--'; Item = "${testFile}" },
@{ Mode = '1644'; Perm = '-rw-r--r-T'; Item = "${testFile}" },
@{ Mode = '2644'; Perm = '-rw-r-Sr--'; Item = "${testFile}" },
@{ Mode = '7644'; Perm = '-rwSr-Sr-T'; Item = "${testFile}" },
@{ Mode = '4777'; Perm = '-rwsrwxrwx'; Item = "${testFile}" },
@{ Mode = '1777'; Perm = 'drwxrwxrwt'; Item = "${testDir}" }
}
Expand All @@ -57,9 +61,16 @@ Describe "UnixFileSystem additions" -Tag "CI" {

It "Should present filemode '<Mode>' string correctly as '<Perm>'" -testCase $testCase {
param ($Mode, $Perm, $Item )
# chmod can fail for some modes so be sure to handle that here.
# specifically, when setting setgid chmod can fail if the group is privileged.
chmod "$Mode" "${Item}"
$i = Get-Item $Item
$i.UnixMode | Should -Be $Perm
if ($LASTEXITCODE -ne 0) {
set-itresult -skip -because "chmod '$mode' failed"
}
else {
$i = Get-Item $Item
$i.UnixMode | Should -BeExactly $Perm
}
}

It "Should retrieve the user name for the file" {
Expand Down