From 47bc8d33c13e06211ec0d77b646a9fd6fc5bd527 Mon Sep 17 00:00:00 2001 From: carlossanlop Date: Mon, 23 Jan 2023 18:24:24 -0800 Subject: [PATCH 1/7] Do not throw if a unix group is non-existent when creating a TarEntry from a file using TarWriter. --- .../Unix/System.Native/Interop.GetGroupName.cs | 12 +++++++++--- .../src/System/Formats/Tar/TarWriter.Unix.cs | 6 ++++-- 2 files changed, 13 insertions(+), 5 deletions(-) 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 index fadbf314e4d51f..3536b1fe02a950 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs @@ -7,17 +7,23 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.IO; internal static partial class Interop { internal static partial class Sys { /// - /// Gets the group name associated to the specified group ID. + /// Tries to get 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()); + /// When this method returns true, gets the value of the group name associated with the specified id. On failure, it is null. + /// On success, return true. On failure, returns false. + internal static bool TryGetGroupName(uint gid, out string? groupName) + { + groupName = GetGroupNameInternal(gid); + return groupName != null; + } [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetGroupName", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] private static unsafe partial string? GetGroupNameInternal(uint uid); 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 41c1b0bb9976f8..f66a983778526e 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 @@ -82,8 +82,10 @@ private TarEntry ConstructEntryForWriting(string fullPath, string entryName, Fil 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); + if (Interop.Sys.TryGetGroupName(status.Gid, out gName) && gName != null) + { + _groupIdentifiers.Add(status.Gid, gName); + } } entry._header._gName = gName; From f61c2bf1b45264ccb3df3c2c7350177b02841b42 Mon Sep 17 00:00:00 2001 From: carlossanlop Date: Mon, 23 Jan 2023 18:24:44 -0800 Subject: [PATCH 2/7] Adjust test that was consuming removed interop method. --- .../tests/TarWriter/TarWriter.File.Base.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs index 8b613191b0ef87..de159971f504f4 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs @@ -20,7 +20,7 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) if (entry is PosixTarEntry posix) { - string gname = Interop.Sys.GetGroupName(status.Gid); + Interop.Sys.TryGetGroupName(status.Gid, out string gname); string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid); Assert.Equal(gname, posix.GroupName); From 7ef7a7c15bc39d8be812a3a36902ce06c6b28b4b Mon Sep 17 00:00:00 2001 From: carlossanlop <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 23 Jan 2023 21:00:30 -0800 Subject: [PATCH 3/7] Add unit tests to add file entry whose group owner does not exist. --- .../TarWriter/TarWriter.File.Base.Unix.cs | 64 +++++++++++++++++++ .../TarWriter.WriteEntry.File.Tests.Unix.cs | 35 ++++++++++ ...rWriter.WriteEntryAsync.File.Tests.Unix.cs | 35 ++++++++++ 3 files changed, 134 insertions(+) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs index de159971f504f4..2a39ec47403acd 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.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.Diagnostics; using System.IO; using Xunit; @@ -51,5 +52,68 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) } } } + + protected int CreateGroup(string groupName) + { + int exitCode = Execute("groupadd", groupName, out string standardOutput, out string standardError); + if (exitCode != 0) + { + ThrowOnError(exitCode, "groupadd", groupName, standardError); + } + return GetGroupId(groupName); + } + + protected int GetGroupId(string groupName) + { + int exitCode = Execute("getent", $"group {groupName}", out string standardOutput, out string standardError); + if (exitCode != 0) + { + ThrowOnError(exitCode, "getent", "group", standardError); + } + + string[] values = standardOutput.Split(':', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + return int.Parse(values[^1]); + } + + protected void SetGroupAsOwnerOfFile(string groupName, string filePath) + { + int exitCode = Execute("chgrp", $"{groupName} {filePath}", out string standardOutput, out string standardError); + if (exitCode != 0) + { + ThrowOnError(exitCode, "chgroup", $"{groupName} {filePath}", standardError); + } + } + + protected void DeleteGroup(string groupName) + { + int exitCode = Execute("groupdel", groupName, out string standardOutput, out string standardError); + if (exitCode != 0) + { + ThrowOnError(exitCode, "groupdel", groupName, standardError); + } + } + + private int Execute(string command, string arguments, out string standardOutput, out string standardError) + { + using Process p = new Process(); + + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = command; + p.StartInfo.Arguments = arguments; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + p.Start(); + p.WaitForExit(); + + standardOutput = p.StandardOutput.ReadToEnd(); + standardError = p.StandardError.ReadToEnd(); + return p.ExitCode; + } + + private void ThrowOnError(int code, string command, string arguments, string message) + { + throw new IOException($"Error '{code}' when executing '{command} {arguments}'. Message: {message}"); + } } } 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 f9dfde9f3bacd2..619c4b41d8e837 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 @@ -139,5 +139,40 @@ public void Add_CharacterDevice(TarEntryFormat format) }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } + + [ConditionalFact(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] + public void CreateEntryFromFileOwnedByNonExistentGroup() + { + RemoteExecutor.Invoke(() => + { + string groupName = Path.GetRandomFileName()[0..6]; + int groupId = CreateGroup(groupName); + + using TempDirectory root = new TempDirectory(); + + string fileName = "file.txt"; + string filePath = Path.Join(root.Path, fileName); + File.Create(filePath).Dispose(); + + SetGroupAsOwnerOfFile(groupName, filePath); + + DeleteGroup(groupName); + + using MemoryStream archive = new MemoryStream(); + using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) + { + writer.WriteEntry(filePath, fileName); // Should not throw + } + archive.Seek(0, SeekOrigin.Begin); + + using (TarReader reader = new TarReader(archive, leaveOpen: false)) + { + UstarTarEntry entry = reader.GetNextEntry() as UstarTarEntry; + Assert.NotNull(entry); + Assert.Equal(entry.GroupName, string.Empty); + Assert.Equal(groupId, entry.Gid); + } + }, new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); + } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs index 771f7ab2620e85..9b3ceb0655dca4 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs @@ -149,5 +149,40 @@ public void Add_CharacterDevice_Async(TarEntryFormat format) } }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } + + [ConditionalFact(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] + public void CreateEntryFromFileOwnedByNonExistentGroup_Async() + { + RemoteExecutor.Invoke(async () => + { + string groupName = Path.GetRandomFileName()[0..6]; + int groupId = CreateGroup(groupName); + + using TempDirectory root = new TempDirectory(); + + string fileName = "file.txt"; + string filePath = Path.Join(root.Path, fileName); + File.Create(filePath).Dispose(); + + SetGroupAsOwnerOfFile(groupName, filePath); + + DeleteGroup(groupName); + + await using MemoryStream archive = new MemoryStream(); + await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) + { + await writer.WriteEntryAsync(filePath, fileName); // Should not throw + } + archive.Seek(0, SeekOrigin.Begin); + + await using (TarReader reader = new TarReader(archive, leaveOpen: false)) + { + UstarTarEntry entry = await reader.GetNextEntryAsync() as UstarTarEntry; + Assert.NotNull(entry); + Assert.Equal(entry.GroupName, string.Empty); + Assert.Equal(groupId, entry.Gid); + } + }, new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); + } } } From a4ece965f5bfc5f6b609709d05cc34cdf77d8c84 Mon Sep 17 00:00:00 2001 From: carlossanlop <1175054+carlossanlop@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:17:48 -0800 Subject: [PATCH 4/7] Address src feedback. --- .../Interop/Unix/System.Native/Interop.GetGroupName.cs | 9 +++++---- .../src/System/Formats/Tar/TarWriter.Unix.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) 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 index 3536b1fe02a950..f36935ae7f39ae 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroupName.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Reflection; using System.IO; +using System.Diagnostics.CodeAnalysis; internal static partial class Interop { @@ -18,14 +19,14 @@ internal static partial class Sys /// /// The group ID. /// When this method returns true, gets the value of the group name associated with the specified id. On failure, it is null. - /// On success, return true. On failure, returns false. - internal static bool TryGetGroupName(uint gid, out string? groupName) + /// On success, returns true. On failure, returns false. + internal static bool TryGetGroupName(uint gid, [NotNullWhen(returnValue: true)] out string? groupName) { - groupName = GetGroupNameInternal(gid); + groupName = GetGroupName(gid); return groupName != null; } [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetGroupName", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] - private static unsafe partial string? GetGroupNameInternal(uint uid); + private static unsafe partial string? GetGroupName(uint uid); } } 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 f66a983778526e..de3aeb31cb8920 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 @@ -82,7 +82,7 @@ private TarEntry ConstructEntryForWriting(string fullPath, string entryName, Fil entry._header._gid = (int)status.Gid; if (!_groupIdentifiers.TryGetValue(status.Gid, out string? gName)) { - if (Interop.Sys.TryGetGroupName(status.Gid, out gName) && gName != null) + if (Interop.Sys.TryGetGroupName(status.Gid, out gName)) { _groupIdentifiers.Add(status.Gid, gName); } From 4ea753e429d1c6743b74525ad316f2b6f8b30b5c Mon Sep 17 00:00:00 2001 From: carlossanlop <1175054+carlossanlop@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:29:54 -0800 Subject: [PATCH 5/7] Address test feedback --- .../TarWriter/TarWriter.File.Base.Unix.cs | 55 ++++++------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs index 2a39ec47403acd..4bdb470ce2ae04 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.File.Base.Unix.cs @@ -21,7 +21,7 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) if (entry is PosixTarEntry posix) { - Interop.Sys.TryGetGroupName(status.Gid, out string gname); + Assert.True(Interop.Sys.TryGetGroupName(status.Gid, out string gname)); string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid); Assert.Equal(gname, posix.GroupName); @@ -55,46 +55,25 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) protected int CreateGroup(string groupName) { - int exitCode = Execute("groupadd", groupName, out string standardOutput, out string standardError); - if (exitCode != 0) - { - ThrowOnError(exitCode, "groupadd", groupName, standardError); - } + Execute("groupadd", groupName); return GetGroupId(groupName); } protected int GetGroupId(string groupName) { - int exitCode = Execute("getent", $"group {groupName}", out string standardOutput, out string standardError); - if (exitCode != 0) - { - ThrowOnError(exitCode, "getent", "group", standardError); - } - + string standardOutput = Execute("getent", $"group {groupName}"); string[] values = standardOutput.Split(':', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - return int.Parse(values[^1]); } - protected void SetGroupAsOwnerOfFile(string groupName, string filePath) - { - int exitCode = Execute("chgrp", $"{groupName} {filePath}", out string standardOutput, out string standardError); - if (exitCode != 0) - { - ThrowOnError(exitCode, "chgroup", $"{groupName} {filePath}", standardError); - } - } + protected void SetGroupAsOwnerOfFile(string groupName, string filePath) => + Execute("chgrp", $"{groupName} {filePath}"); - protected void DeleteGroup(string groupName) - { - int exitCode = Execute("groupdel", groupName, out string standardOutput, out string standardError); - if (exitCode != 0) - { - ThrowOnError(exitCode, "groupdel", groupName, standardError); - } - } - private int Execute(string command, string arguments, out string standardOutput, out string standardError) + protected void DeleteGroup(string groupName) => + Execute("groupdel", groupName); + + private string Execute(string command, string arguments) { using Process p = new Process(); @@ -103,17 +82,19 @@ private int Execute(string command, string arguments, out string standardOutput, p.StartInfo.Arguments = arguments; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; + p.Start(); p.WaitForExit(); - standardOutput = p.StandardOutput.ReadToEnd(); - standardError = p.StandardError.ReadToEnd(); - return p.ExitCode; - } + string standardOutput = p.StandardOutput.ReadToEnd(); + string standardError = p.StandardError.ReadToEnd(); + + if (p.ExitCode != 0) + { + throw new IOException($"Error '{p.ExitCode}' when executing '{command} {arguments}'. Message: {standardError}"); + } - private void ThrowOnError(int code, string command, string arguments, string message) - { - throw new IOException($"Error '{code}' when executing '{command} {arguments}'. Message: {message}"); + return standardOutput; } } } From 3370d9d14656d4e1f03ba27f9cd44948b2be5397 Mon Sep 17 00:00:00 2001 From: carlossanlop <1175054+carlossanlop@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:33:28 -0800 Subject: [PATCH 6/7] Delay creation/deletion of group in test --- .../TarWriter.WriteEntry.File.Tests.Unix.cs | 15 ++++++++++----- .../TarWriter.WriteEntryAsync.File.Tests.Unix.cs | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) 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 619c4b41d8e837..c712e088265008 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 @@ -145,18 +145,23 @@ public void CreateEntryFromFileOwnedByNonExistentGroup() { RemoteExecutor.Invoke(() => { - string groupName = Path.GetRandomFileName()[0..6]; - int groupId = CreateGroup(groupName); - using TempDirectory root = new TempDirectory(); string fileName = "file.txt"; string filePath = Path.Join(root.Path, fileName); File.Create(filePath).Dispose(); - SetGroupAsOwnerOfFile(groupName, filePath); + string groupName = Path.GetRandomFileName()[0..6]; + int groupId = CreateGroup(groupName); - DeleteGroup(groupName); + try + { + SetGroupAsOwnerOfFile(groupName, filePath); + } + finally + { + DeleteGroup(groupName); + } using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs index 9b3ceb0655dca4..2916c45756a41b 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs @@ -155,18 +155,23 @@ public void CreateEntryFromFileOwnedByNonExistentGroup_Async() { RemoteExecutor.Invoke(async () => { - string groupName = Path.GetRandomFileName()[0..6]; - int groupId = CreateGroup(groupName); - using TempDirectory root = new TempDirectory(); string fileName = "file.txt"; string filePath = Path.Join(root.Path, fileName); File.Create(filePath).Dispose(); - SetGroupAsOwnerOfFile(groupName, filePath); + string groupName = Path.GetRandomFileName()[0..6]; + int groupId = CreateGroup(groupName); - DeleteGroup(groupName); + try + { + SetGroupAsOwnerOfFile(groupName, filePath); + } + finally + { + DeleteGroup(groupName); + } await using MemoryStream archive = new MemoryStream(); await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) From 9e601ef140901c69061b301ac5bc304860dc2dbb Mon Sep 17 00:00:00 2001 From: carlossanlop <1175054+carlossanlop@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:48:46 -0800 Subject: [PATCH 7/7] Test all PosixTarEntry formats --- .../TarWriter.WriteEntry.File.Tests.Unix.cs | 16 ++++++++++------ .../TarWriter.WriteEntryAsync.File.Tests.Unix.cs | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) 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 c712e088265008..df9f842152c822 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 @@ -140,10 +140,13 @@ public void Add_CharacterDevice(TarEntryFormat format) }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } - [ConditionalFact(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] - public void CreateEntryFromFileOwnedByNonExistentGroup() + [ConditionalTheory(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] + [InlineData(TarEntryFormat.Ustar)] + [InlineData(TarEntryFormat.Pax)] + [InlineData(TarEntryFormat.Gnu)] + public void CreateEntryFromFileOwnedByNonExistentGroup(TarEntryFormat f) { - RemoteExecutor.Invoke(() => + RemoteExecutor.Invoke((string strFormat) => { using TempDirectory root = new TempDirectory(); @@ -164,7 +167,7 @@ public void CreateEntryFromFileOwnedByNonExistentGroup() } using MemoryStream archive = new MemoryStream(); - using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) + using (TarWriter writer = new TarWriter(archive, Enum.Parse(strFormat), leaveOpen: true)) { writer.WriteEntry(filePath, fileName); // Should not throw } @@ -172,12 +175,13 @@ public void CreateEntryFromFileOwnedByNonExistentGroup() using (TarReader reader = new TarReader(archive, leaveOpen: false)) { - UstarTarEntry entry = reader.GetNextEntry() as UstarTarEntry; + PosixTarEntry entry = reader.GetNextEntry() as PosixTarEntry; Assert.NotNull(entry); Assert.Equal(entry.GroupName, string.Empty); Assert.Equal(groupId, entry.Gid); + Assert.Null(reader.GetNextEntry()); } - }, new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); + }, f.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } } } diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs index 2916c45756a41b..97d667f896b376 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntryAsync.File.Tests.Unix.cs @@ -150,10 +150,13 @@ public void Add_CharacterDevice_Async(TarEntryFormat format) }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } - [ConditionalFact(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] - public void CreateEntryFromFileOwnedByNonExistentGroup_Async() + [ConditionalTheory(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] + [InlineData(TarEntryFormat.Ustar)] + [InlineData(TarEntryFormat.Pax)] + [InlineData(TarEntryFormat.Gnu)] + public void CreateEntryFromFileOwnedByNonExistentGroup_Async(TarEntryFormat f) { - RemoteExecutor.Invoke(async () => + RemoteExecutor.Invoke(async (string strFormat) => { using TempDirectory root = new TempDirectory(); @@ -174,7 +177,7 @@ public void CreateEntryFromFileOwnedByNonExistentGroup_Async() } await using MemoryStream archive = new MemoryStream(); - await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true)) + await using (TarWriter writer = new TarWriter(archive, Enum.Parse(strFormat), leaveOpen: true)) { await writer.WriteEntryAsync(filePath, fileName); // Should not throw } @@ -182,12 +185,13 @@ public void CreateEntryFromFileOwnedByNonExistentGroup_Async() await using (TarReader reader = new TarReader(archive, leaveOpen: false)) { - UstarTarEntry entry = await reader.GetNextEntryAsync() as UstarTarEntry; + PosixTarEntry entry = await reader.GetNextEntryAsync() as PosixTarEntry; Assert.NotNull(entry); Assert.Equal(entry.GroupName, string.Empty); Assert.Equal(groupId, entry.Gid); + Assert.Null(await reader.GetNextEntryAsync()); } - }, new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); + }, f.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } } }