Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 5.6k
Fix TarWriter.TarEntry exception when adding file whose Unix group owner does not exist in the system#81070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
47bc8d3f61c2bf7ef7a7ca4ece964ea753e3370d9d9e601efFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| @@ -20,7 +21,7 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) | ||
| if (entry is PosixTarEntry posix) | ||
| { | ||
| string gname = Interop.Sys.GetGroupName(status.Gid); | ||
| Assert.True(Interop.Sys.TryGetGroupName(status.Gid, out string gname)); | ||
| string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid); | ||
| Assert.Equal(gname, posix.GroupName); | ||
| @@ -51,5 +52,49 @@ protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) | ||
| } | ||
| } | ||
| } | ||
| protected int CreateGroup(string groupName) | ||
| { | ||
| Execute("groupadd", groupName); | ||
| return GetGroupId(groupName); | ||
| } | ||
| protected int GetGroupId(string groupName) | ||
| { | ||
| 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) => | ||
| Execute("chgrp", $"{groupName} {filePath}"); | ||
| protected void DeleteGroup(string groupName) => | ||
| Execute("groupdel", groupName); | ||
| private string Execute(string command, string arguments) | ||
| { | ||
| 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(); | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is using the pattern that is prone to a famous deadlock it will only occur if there is a sufficiently large amount of output, so this could well be fine here, but just FYI that this is not a good pattern to use in general. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Better late than never: #83126 | ||
| 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}"); | ||
| } | ||
| return standardOutput; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -149,5 +149,49 @@ public void Add_CharacterDevice_Async(TarEntryFormat format) | ||
| } | ||
| }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); | ||
| } | ||
| [ConditionalTheory(nameof(IsRemoteExecutorSupportedAndPrivilegedProcess))] | ||
| [InlineData(TarEntryFormat.Ustar)] | ||
| [InlineData(TarEntryFormat.Pax)] | ||
| [InlineData(TarEntryFormat.Gnu)] | ||
| public void CreateEntryFromFileOwnedByNonExistentGroup_Async(TarEntryFormat f) | ||
| { | ||
| RemoteExecutor.Invoke(async (string strFormat) => | ||
| { | ||
| using TempDirectory root = new TempDirectory(); | ||
| string fileName = "file.txt"; | ||
| string filePath = Path.Join(root.Path, fileName); | ||
| File.Create(filePath).Dispose(); | ||
| string groupName = Path.GetRandomFileName()[0..6]; | ||
| int groupId = CreateGroup(groupName); | ||
| try | ||
| { | ||
| SetGroupAsOwnerOfFile(groupName, filePath); | ||
| } | ||
| finally | ||
| { | ||
| DeleteGroup(groupName); | ||
| } | ||
| await using MemoryStream archive = new MemoryStream(); | ||
| await using (TarWriter writer = new TarWriter(archive, Enum.Parse<TarEntryFormat>(strFormat), leaveOpen: true)) | ||
| { | ||
| await writer.WriteEntryAsync(filePath, fileName); // Should not throw | ||
| } | ||
| archive.Seek(0, SeekOrigin.Begin); | ||
| await using (TarReader reader = new TarReader(archive, leaveOpen: false)) | ||
| { | ||
| PosixTarEntry entry = await reader.GetNextEntryAsync() as PosixTarEntry; | ||
| Assert.NotNull(entry); | ||
| Assert.Equal(entry.GroupName, string.Empty); | ||
jozkee marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Assert.Equal(groupId, entry.Gid); | ||
carlossanlop marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Assert.Null(await reader.GetNextEntryAsync()); | ||
| } | ||
| }, f.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.