diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DeviceFiles.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DeviceFiles.cs
index b212db6417c41a..dc8c566a709a00 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DeviceFiles.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DeviceFiles.cs
@@ -23,6 +23,6 @@ internal static int CreateCharacterDevice(string pathName, uint mode, uint major
private static partial int MkNod(string pathName, uint mode, uint major, uint minor);
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDeviceIdentifiers", SetLastError = true)]
- internal static unsafe partial int GetDeviceIdentifiers(ulong dev, uint* majorNumber, uint* minorNumber);
+ internal static unsafe partial void GetDeviceIdentifiers(ulong dev, uint* majorNumber, uint* minorNumber);
}
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs
new file mode 100644
index 00000000000000..fadbf314e4d51f
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using System.Buffers;
+using System.Text;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ ///
+ /// Gets the group name associated to the specified group ID.
+ ///
+ /// The group ID.
+ /// On success, return a string with the group name. On failure, throws an IOException.
+ internal static string GetGroupName(uint gid) => GetGroupNameInternal(gid) ?? throw GetIOException(GetLastErrorInfo());
+
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetGroupName", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
+ private static unsafe partial string? GetGroupNameInternal(uint uid);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
index 79b3b4fa396b37..6318a94305884e 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
@@ -1,6 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
internal static partial class Interop
@@ -20,6 +24,75 @@ internal unsafe struct Passwd
internal byte* Shell;
}
+ ///
+ /// Gets the user name associated to the specified UID.
+ ///
+ /// The user ID.
+ /// On success, return a string with the user name associated to the specified UID. On failure, returns an empty string.
+ internal static unsafe string GetUserNameFromPasswd(uint uid)
+ {
+ // First try with a buffer that should suffice for 99% of cases.
+ string? username;
+ const int BufLen = Interop.Sys.Passwd.InitialBufferSize;
+ byte* stackBuf = stackalloc byte[BufLen];
+ if (TryGetUserNameFromPasswd(uid, stackBuf, BufLen, out username))
+ {
+ return username ?? string.Empty;
+ }
+
+ // Fallback to heap allocations if necessary, growing the buffer until
+ // we succeed. TryGetUserNameFromPasswd will throw if there's an unexpected error.
+ int lastBufLen = BufLen;
+ while (true)
+ {
+ lastBufLen *= 2;
+ byte[] heapBuf = new byte[lastBufLen];
+ fixed (byte* buf = &heapBuf[0])
+ {
+ if (TryGetUserNameFromPasswd(uid, buf, heapBuf.Length, out username))
+ {
+ return username ?? string.Empty;
+ }
+ }
+ }
+ }
+
+ private static unsafe bool TryGetUserNameFromPasswd(uint uid, byte* buf, int bufLen, out string? username)
+ {
+ // Call getpwuid_r to get the passwd struct
+ Interop.Sys.Passwd passwd;
+ int error = Interop.Sys.GetPwUidR(uid, out passwd, buf, bufLen);
+
+ // If the call succeeds, give back the user name retrieved
+ if (error == 0)
+ {
+ Debug.Assert(passwd.Name != null);
+ username = Marshal.PtrToStringAnsi((IntPtr)passwd.Name);
+ return true;
+ }
+
+ // If the current user's entry could not be found, give back null,
+ // but still return true (false indicates the buffer was too small).
+ if (error == -1)
+ {
+ username = null;
+ return true;
+ }
+
+ var errorInfo = new Interop.ErrorInfo(error);
+
+ // If the call failed because the buffer was too small, return false to
+ // indicate the caller should try again with a larger buffer.
+ if (errorInfo.Error == Interop.Error.ERANGE)
+ {
+ username = null;
+ return false;
+ }
+
+ // Otherwise, fail.
+ throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
+ }
+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPwUidR", SetLastError = false)]
internal static unsafe partial int GetPwUidR(uint uid, out Passwd pwd, byte* buf, int bufLen);
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
index b5136bbfe27771..0796599a096497 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
@@ -31,6 +31,7 @@ internal struct FileStatus
internal long BirthTime;
internal long BirthTimeNsec;
internal long Dev;
+ internal long RDev;
internal long Ino;
internal uint UserFlags;
}
diff --git a/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj b/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj
index 1fab17860d4584..d866dc3e3f96ca 100644
--- a/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj
+++ b/src/libraries/System.Formats.Tar/src/System.Formats.Tar.csproj
@@ -57,6 +57,9 @@
+
+
+
diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs
index 0800f39ecf0285..ce5e076cfc38ae 100644
--- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs
+++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs
@@ -431,7 +431,7 @@ private void ExtractAsRegularFile(string destinationFileName)
{
Debug.Assert(!Path.Exists(destinationFileName));
- FileStreamOptions fileStreamOptions = new FileStreamOptions()
+ FileStreamOptions fileStreamOptions = new()
{
Access = FileAccess.Write,
Mode = FileMode.CreateNew,
diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs
index d23eb1f92a19b7..ba56ac87441614 100644
--- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs
+++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs
@@ -72,12 +72,8 @@ public static void CreateFromDirectory(string sourceDirectoryName, string destin
throw new DirectoryNotFoundException(string.Format(SR.IO_PathNotFound_Path, sourceDirectoryName));
}
- if (Path.Exists(destinationFileName))
- {
- throw new IOException(string.Format(SR.IO_FileExists_Name, destinationFileName));
- }
-
- using FileStream fs = File.Create(destinationFileName, bufferSize: 0x1000, FileOptions.None);
+ // Throws if the destination file exists
+ using FileStream fs = new(destinationFileName, FileMode.CreateNew, FileAccess.Write);
CreateFromDirectoryInternal(sourceDirectoryName, fs, includeBaseDirectory, leaveOpen: false);
}
@@ -170,15 +166,7 @@ public static void ExtractToDirectory(string sourceFileName, string destinationD
throw new DirectoryNotFoundException(string.Format(SR.IO_PathNotFound_Path, destinationDirectoryName));
}
- FileStreamOptions fileStreamOptions = new()
- {
- Access = FileAccess.Read,
- BufferSize = 0x1000,
- Mode = FileMode.Open,
- Share = FileShare.Read
- };
-
- using FileStream archive = File.Open(sourceFileName, fileStreamOptions);
+ using FileStream archive = File.OpenRead(sourceFileName);
ExtractToDirectoryInternal(archive, destinationDirectoryName, overwriteFiles, leaveOpen: false);
}
diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs
index dffa8525a9405e..7fa73a3cf13cb5 100644
--- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs
+++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -9,6 +10,9 @@ namespace System.Formats.Tar
// Unix specific methods for the TarWriter class.
public sealed partial class TarWriter : IDisposable
{
+ private readonly Dictionary _userIdentifiers = new Dictionary();
+ private readonly Dictionary _groupIdentifiers = new Dictionary();
+
// Unix specific implementation of the method that reads an entry from disk and writes it into the archive stream.
partial void ReadFileFromDiskAndWriteToArchiveStreamAsEntry(string fullPath, string entryName)
{
@@ -41,13 +45,13 @@ partial void ReadFileFromDiskAndWriteToArchiveStreamAsEntry(string fullPath, str
_ => throw new FormatException(string.Format(SR.TarInvalidFormat, Format)),
};
- if ((entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice) && status.Dev > 0)
+ if (entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice)
{
uint major;
uint minor;
unsafe
{
- Interop.CheckIo(Interop.Sys.GetDeviceIdentifiers((ulong)status.Dev, &major, &minor));
+ Interop.Sys.GetDeviceIdentifiers((ulong)status.RDev, &major, &minor);
}
entry._header._devMajor = (int)major;
@@ -60,12 +64,23 @@ partial void ReadFileFromDiskAndWriteToArchiveStreamAsEntry(string fullPath, str
entry._header._mode = (status.Mode & 4095); // First 12 bits
- entry.Uid = (int)status.Uid;
- entry.Gid = (int)status.Gid;
+ // Uid and UName
+ entry._header._uid = (int)status.Uid;
+ if (!_userIdentifiers.TryGetValue(status.Uid, out string? uName))
+ {
+ uName = Interop.Sys.GetUserNameFromPasswd(status.Uid);
+ _userIdentifiers.Add(status.Uid, uName);
+ }
+ entry._header._uName = uName;
- // TODO: Add these p/invokes https://github.com/dotnet/runtime/issues/68230
- entry._header._uName = "";// Interop.Sys.GetUName();
- entry._header._gName = "";// Interop.Sys.GetGName();
+ // Gid and GName
+ entry._header._gid = (int)status.Gid;
+ if (!_groupIdentifiers.TryGetValue(status.Gid, out string? gName))
+ {
+ gName = Interop.Sys.GetGroupName(status.Gid);
+ _groupIdentifiers.Add(status.Gid, gName);
+ }
+ entry._header._gName = gName;
if (entry.EntryType == TarEntryType.SymbolicLink)
{
@@ -74,16 +89,8 @@ partial void ReadFileFromDiskAndWriteToArchiveStreamAsEntry(string fullPath, str
if (entry.EntryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile)
{
- FileStreamOptions options = new()
- {
- Mode = FileMode.Open,
- Access = FileAccess.Read,
- Share = FileShare.Read,
- Options = FileOptions.None
- };
-
Debug.Assert(entry._header._dataStream == null);
- entry._header._dataStream = File.Open(fullPath, options);
+ entry._header._dataStream = File.OpenRead(fullPath);
}
WriteEntry(entry);
diff --git a/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj b/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
index 63240f7bd9094a..d41bc4a1f1d118 100644
--- a/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
+++ b/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
@@ -56,6 +56,9 @@
+
+
+
@@ -66,7 +69,4 @@
-
-
-
diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
index 67466ca3444044..70453f4d49931e 100644
--- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
@@ -100,10 +100,7 @@ public void Extract_Archive_File_OverwriteFalse()
string filePath = Path.Join(destination.Path, "file.txt");
- using (StreamWriter writer = File.CreateText(filePath))
- {
- writer.WriteLine("My existence should cause an exception");
- }
+ File.Create(filePath).Dispose();
Assert.Throws(() => TarFile.ExtractToDirectory(sourceArchiveFileName, destination.Path, overwriteFiles: false));
}
diff --git a/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.File.Tests.cs
index fa9ea86ed9d43f..ef619edb472f96 100644
--- a/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.File.Tests.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.File.Tests.cs
@@ -713,13 +713,8 @@ private void Verify_Archive_BlockDevice(PosixTarEntry blockDevice, IReadOnlyDict
Assert.True(blockDevice.ModificationTime > DateTimeOffset.UnixEpoch);
Assert.Equal(expectedFileName, blockDevice.Name);
Assert.Equal(AssetUid, blockDevice.Uid);
-
- // TODO: Figure out why the numbers don't match https://github.com/dotnet/runtime/issues/68230
- // Assert.Equal(AssetBlockDeviceMajor, blockDevice.DeviceMajor);
- // Assert.Equal(AssetBlockDeviceMinor, blockDevice.DeviceMinor);
- // Remove these two temporary checks when the above is fixed
- Assert.True(blockDevice.DeviceMajor > 0);
- Assert.True(blockDevice.DeviceMinor > 0);
+ Assert.Equal(AssetBlockDeviceMajor, blockDevice.DeviceMajor);
+ Assert.Equal(AssetBlockDeviceMinor, blockDevice.DeviceMinor);
Assert.Equal(AssetGName, blockDevice.GroupName);
Assert.Equal(AssetUName, blockDevice.UserName);
@@ -749,13 +744,8 @@ private void Verify_Archive_CharacterDevice(PosixTarEntry characterDevice, IRead
Assert.True(characterDevice.ModificationTime > DateTimeOffset.UnixEpoch);
Assert.Equal(expectedFileName, characterDevice.Name);
Assert.Equal(AssetUid, characterDevice.Uid);
-
- // TODO: Figure out why the numbers don't match https://github.com/dotnet/runtime/issues/68230
- //Assert.Equal(AssetBlockDeviceMajor, characterDevice.DeviceMajor);
- //Assert.Equal(AssetBlockDeviceMinor, characterDevice.DeviceMinor);
- // Remove these two temporary checks when the above is fixed
- Assert.True(characterDevice.DeviceMajor > 0);
- Assert.True(characterDevice.DeviceMinor > 0);
+ Assert.Equal(AssetCharacterDeviceMajor, characterDevice.DeviceMajor);
+ Assert.Equal(AssetCharacterDeviceMinor, characterDevice.DeviceMinor);
Assert.Equal(AssetGName, characterDevice.GroupName);
Assert.Equal(AssetUName, characterDevice.UserName);
diff --git a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
index 3df9bf89cc1fe3..fb0467593d18e1 100644
--- a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
@@ -99,15 +99,8 @@ protected static string GetTarFilePath(CompressionMethod compressionMethod, Test
protected static MemoryStream GetTarMemoryStream(CompressionMethod compressionMethod, TestTarFormat format, string testCaseName)
{
string path = GetTarFilePath(compressionMethod, format, testCaseName);
- FileStreamOptions options = new()
- {
- Access = FileAccess.Read,
- Mode = FileMode.Open,
- Share = FileShare.Read
-
- };
MemoryStream ms = new();
- using (FileStream fs = new FileStream(path, options))
+ using (FileStream fs = File.OpenRead(path))
{
fs.CopyTo(ms);
}
diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.File.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.File.Tests.Unix.cs
index 2c57ef21f9b162..95239178941661 100644
--- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.File.Tests.Unix.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.File.Tests.Unix.cs
@@ -91,10 +91,8 @@ public void Add_BlockDevice(TarFormat format)
VerifyPlatformSpecificMetadata(blockDevicePath, entry);
- // TODO: Fix how these values are collected, the numbers don't match even though https://github.com/dotnet/runtime/issues/68230
- // they come from stat's dev and from the major/minor syscalls
- // Assert.Equal(TestBlockDeviceMajor, entry.DeviceMajor);
- // Assert.Equal(TestBlockDeviceMinor, entry.DeviceMinor);
+ Assert.Equal(TestBlockDeviceMajor, entry.DeviceMajor);
+ Assert.Equal(TestBlockDeviceMinor, entry.DeviceMinor);
Assert.Null(reader.GetNextEntry());
}
@@ -138,10 +136,8 @@ public void Add_CharacterDevice(TarFormat format)
VerifyPlatformSpecificMetadata(characterDevicePath, entry);
- // TODO: Fix how these values are collected, the numbers don't match even though https://github.com/dotnet/runtime/issues/68230
- // they come from stat's dev and from the major/minor syscalls
- // Assert.Equal(TestCharacterDeviceMajor, entry.DeviceMajor);
- // Assert.Equal(TestCharacterDeviceMinor, entry.DeviceMinor);
+ Assert.Equal(TestCharacterDeviceMajor, entry.DeviceMajor);
+ Assert.Equal(TestCharacterDeviceMinor, entry.DeviceMinor);
Assert.Null(reader.GetNextEntry());
}
@@ -161,8 +157,11 @@ partial void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry)
if (entry is PosixTarEntry posix)
{
- Assert.Equal(DefaultGName, posix.GroupName);
- Assert.Equal(DefaultUName, posix.UserName);
+ string gname = Interop.Sys.GetGroupName(status.Gid);
+ string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid);
+
+ Assert.Equal(gname, posix.GroupName);
+ Assert.Equal(uname, posix.UserName);
if (entry.EntryType is not TarEntryType.BlockDevice and not TarEntryType.CharacterDevice)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
index 50393d6b1e37c0..e1ecca767122df 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
@@ -24,73 +24,7 @@ public static string MachineName
}
}
- public static unsafe string UserName
- {
- get
- {
- // First try with a buffer that should suffice for 99% of cases.
- string? username;
- const int BufLen = Interop.Sys.Passwd.InitialBufferSize;
- byte* stackBuf = stackalloc byte[BufLen];
- if (TryGetUserNameFromPasswd(stackBuf, BufLen, out username))
- {
- return username ?? string.Empty;
- }
-
- // Fallback to heap allocations if necessary, growing the buffer until
- // we succeed. TryGetUserNameFromPasswd will throw if there's an unexpected error.
- int lastBufLen = BufLen;
- while (true)
- {
- lastBufLen *= 2;
- byte[] heapBuf = new byte[lastBufLen];
- fixed (byte* buf = &heapBuf[0])
- {
- if (TryGetUserNameFromPasswd(buf, heapBuf.Length, out username))
- {
- return username ?? string.Empty;
- }
- }
- }
-
- }
- }
-
- private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out string? username)
- {
- // Call getpwuid_r to get the passwd struct
- Interop.Sys.Passwd passwd;
- int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen);
-
- // If the call succeeds, give back the user name retrieved
- if (error == 0)
- {
- Debug.Assert(passwd.Name != null);
- username = Marshal.PtrToStringAnsi((IntPtr)passwd.Name);
- return true;
- }
-
- // If the current user's entry could not be found, give back null,
- // but still return true (false indicates the buffer was too small).
- if (error == -1)
- {
- username = null;
- return true;
- }
-
- var errorInfo = new Interop.ErrorInfo(error);
-
- // If the call failed because the buffer was too small, return false to
- // indicate the caller should try again with a larger buffer.
- if (errorInfo.Error == Interop.Error.ERANGE)
- {
- username = null;
- return false;
- }
-
- // Otherwise, fail.
- throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
- }
+ public static string UserName => Interop.Sys.GetUserNameFromPasswd(Interop.Sys.GetEUid());
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Avoid inlining PInvoke frame into the hot path
private static int GetProcessId() => Interop.Sys.GetPid();
diff --git a/src/native/libs/Common/pal_config.h.in b/src/native/libs/Common/pal_config.h.in
index 8a817a63bde545..2c1c8ff1922c79 100644
--- a/src/native/libs/Common/pal_config.h.in
+++ b/src/native/libs/Common/pal_config.h.in
@@ -136,6 +136,7 @@
#cmakedefine01 HAVE_POSIX_MEMALIGN
#cmakedefine01 HAVE_MAKEDEV_FILEH
#cmakedefine01 HAVE_MAKEDEV_SYSMACROSH
+#cmakedefine01 HAVE_GETGRGID_R
// Mac OS X has stat64, but it is deprecated since plain stat now
// provides the same 64-bit aware struct when targeting OS X > 10.5
diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c
index 6a960a42ae14cb..0155b6426f4019 100644
--- a/src/native/libs/System.Native/entrypoints.c
+++ b/src/native/libs/System.Native/entrypoints.c
@@ -268,6 +268,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_GetEnv)
DllImportEntry(SystemNative_GetEnviron)
DllImportEntry(SystemNative_FreeEnviron)
+ DllImportEntry(SystemNative_GetGroupName)
};
EXTERN_C const void* SystemResolveDllImport(const char* name);
diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c
index a0714a778f196e..d196cbf33970f1 100644
--- a/src/native/libs/System.Native/pal_io.c
+++ b/src/native/libs/System.Native/pal_io.c
@@ -190,6 +190,7 @@ c_static_assert(PAL_IN_ISDIR == IN_ISDIR);
static void ConvertFileStatus(const struct stat_* src, FileStatus* dst)
{
dst->Dev = (int64_t)src->st_dev;
+ dst->RDev = (int64_t)src->st_rdev;
dst->Ino = (int64_t)src->st_ino;
dst->Flags = FILESTATUS_FLAGS_NONE;
dst->Mode = (int32_t)src->st_mode;
@@ -770,23 +771,17 @@ int32_t SystemNative_SymLink(const char* target, const char* linkPath)
return result;
}
-int32_t SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint32_t* minorNumber)
+void SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint32_t* minorNumber)
{
dev_t castedDev = (dev_t)dev;
*majorNumber = (uint32_t)major(castedDev);
*minorNumber = (uint32_t)minor(castedDev);
- return ConvertErrorPlatformToPal(errno);
}
int32_t SystemNative_MkNod(const char* pathName, uint32_t mode, uint32_t major, uint32_t minor)
{
dev_t dev = (dev_t)makedev(major, minor);
- if (errno > 0)
- {
- return -1;
- }
-
int32_t result;
while ((result = mknod(pathName, (mode_t)mode, dev)) < 0 && errno == EINTR);
return result;
diff --git a/src/native/libs/System.Native/pal_io.h b/src/native/libs/System.Native/pal_io.h
index 1ace89303642a6..720d7623fee79f 100644
--- a/src/native/libs/System.Native/pal_io.h
+++ b/src/native/libs/System.Native/pal_io.h
@@ -31,6 +31,7 @@ typedef struct
int64_t BirthTime; // time the file was created
int64_t BirthTimeNsec; // nanosecond part
int64_t Dev; // ID of the device containing the file
+ int64_t RDev; // ID of the device if it is a special file
int64_t Ino; // inode number of the file
uint32_t UserFlags; // user defined flags
} FileStatus;
@@ -543,9 +544,8 @@ PALEXPORT int32_t SystemNative_SymLink(const char* target, const char* linkPath)
/**
* Given a device ID, extracts the major and minor and components and returns them.
- * Return 0 on success; otherwise, returns -1 and errno is set.
*/
-PALEXPORT int32_t SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint32_t* minorNumber);
+PALEXPORT void SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint32_t* minorNumber);
/**
* Creates a special or ordinary file.
diff --git a/src/native/libs/System.Native/pal_uid.c b/src/native/libs/System.Native/pal_uid.c
index e1f64f03d5019a..df921de926af54 100644
--- a/src/native/libs/System.Native/pal_uid.c
+++ b/src/native/libs/System.Native/pal_uid.c
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
#include "pal_config.h"
+#include "pal_safecrt.h"
#include "pal_uid.h"
#include "pal_utilities.h"
@@ -20,7 +21,7 @@
#define USE_GROUPLIST_LOCK
#endif
-#ifdef USE_GROUPLIST_LOCK
+#if defined(USE_GROUPLIST_LOCK) || !HAVE_GETGRGID_R
#include
#endif
@@ -237,3 +238,67 @@ int32_t SystemNative_GetGroups(int32_t ngroups, uint32_t* groups)
return getgroups(ngroups, groups);
}
+
+#if !HAVE_GETGRGID_R
+// Need to call getgrgid which is not thread-safe, and protect it with a mutex
+static pthread_mutex_t s_getgrgid_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+char* SystemNative_GetGroupName(uint32_t gid)
+{
+#if HAVE_GETGRGID_R
+ size_t bufferLength = 512;
+ while (1)
+ {
+ char *buffer = (char*)malloc(bufferLength);
+ if (buffer == NULL)
+ {
+ return NULL;
+ }
+
+ struct group* result;
+ struct group gr;
+ if (getgrgid_r(gid, &gr, buffer, bufferLength, &result) == 0)
+ {
+ if (result == NULL)
+ {
+ errno = ENOENT;
+ free(buffer);
+ return NULL;
+ }
+ else
+ {
+ char* name = strdup(gr.gr_name);
+ free(buffer);
+ return name;
+ }
+ }
+
+ free(buffer);
+ size_t tmpBufferLength;
+ if (errno != ERANGE || !multiply_s(bufferLength, (size_t)2, &tmpBufferLength))
+ {
+ return NULL;
+ }
+ bufferLength = tmpBufferLength;
+ }
+#else
+ // Platforms like Android API level < 24 do not have getgrgid_r available
+ int rv = pthread_mutex_lock(&s_getgrgid_lock);
+ if (rv != 0)
+ {
+ errno = rv;
+ return NULL;
+ }
+
+ struct group* result = getgrgid(gid);
+ if (result == NULL)
+ {
+ pthread_mutex_unlock(&s_getgrgid_lock);
+ return NULL;
+ }
+ char* name = strdup(result->gr_name);
+ pthread_mutex_unlock(&s_getgrgid_lock);
+ return name;
+#endif
+}
diff --git a/src/native/libs/System.Native/pal_uid.h b/src/native/libs/System.Native/pal_uid.h
index b9a24f42304608..f8e0b7d38e056b 100644
--- a/src/native/libs/System.Native/pal_uid.h
+++ b/src/native/libs/System.Native/pal_uid.h
@@ -82,3 +82,12 @@ PALEXPORT int32_t SystemNative_GetGroupList(const char* name, uint32_t group, ui
* If the buffer is too small, errno is EINVAL.
*/
PALEXPORT int32_t SystemNative_GetGroups(int32_t ngroups, uint32_t* groups);
+
+/**
+* Gets the user name associated with the specified group ID and stores it in the buffer.
+* On failure, returns a null char pointer and sets errno.
+* On success, returns a valid char pointer containing the group name.
+* Note that this method returns new memory. Consumers can rely on the marshalling behaviour to free the returned string.
+*/
+PALEXPORT char* SystemNative_GetGroupName(uint32_t gid);
+
diff --git a/src/native/libs/configure.cmake b/src/native/libs/configure.cmake
index fc8427e38af717..8567842366bc5a 100644
--- a/src/native/libs/configure.cmake
+++ b/src/native/libs/configure.cmake
@@ -1115,6 +1115,11 @@ if (NOT HAVE_MAKEDEV_FILEH AND NOT HAVE_MAKEDEV_SYSMACROSH)
message(FATAL_ERROR "Cannot find the makedev function on this platform.")
endif()
+check_symbol_exists(
+ getgrgid_r
+ grp.h
+ HAVE_GETGRGID_R)
+
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Common/pal_config.h.in
${CMAKE_CURRENT_BINARY_DIR}/Common/pal_config.h)