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)