Skip to content

Commit 876c3bd

Browse files
committed
Implement Tar APIs (dotnet#67883)
API proposal: dotnet#65951
1 parent eb9dd67 commit 876c3bd

File tree

78 files changed

+9939
-42
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+9939
-42
lines changed

eng/Version.Details.xml

+4
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@
138138
<Uri>https://github.com/dotnet/runtime-assets</Uri>
139139
<Sha>78cb33dbb0fb5156f049b9e1778f47b508f1be9f</Sha>
140140
</Dependency>
141+
<Dependency Name="System.Formats.Tar.TestData" Version="7.0.0-beta.22214.1">
142+
<Uri>https://github.com/dotnet/runtime-assets</Uri>
143+
<Sha>78cb33dbb0fb5156f049b9e1778f47b508f1be9f</Sha>
144+
</Dependency>
141145
<Dependency Name="System.IO.Compression.TestData" Version="7.0.0-beta.22214.1">
142146
<Uri>https://github.com/dotnet/runtime-assets</Uri>
143147
<Sha>78cb33dbb0fb5156f049b9e1778f47b508f1be9f</Sha>

eng/Versions.props

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
<SystemRuntimeNumericsTestDataVersion>7.0.0-beta.22214.1</SystemRuntimeNumericsTestDataVersion>
131131
<SystemComponentModelTypeConverterTestDataVersion>7.0.0-beta.22214.1</SystemComponentModelTypeConverterTestDataVersion>
132132
<SystemDrawingCommonTestDataVersion>7.0.0-beta.22214.1</SystemDrawingCommonTestDataVersion>
133+
<SystemFormatsTarTestDataVersion>7.0.0-beta.22214.1</SystemFormatsTarTestDataVersion>
133134
<SystemIOCompressionTestDataVersion>7.0.0-beta.22214.1</SystemIOCompressionTestDataVersion>
134135
<SystemIOPackagingTestDataVersion>7.0.0-beta.22214.1</SystemIOPackagingTestDataVersion>
135136
<SystemNetTestDataVersion>7.0.0-beta.22214.1</SystemNetTestDataVersion>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
6+
internal static partial class Interop
7+
{
8+
// mknod: https://man7.org/linux/man-pages/man2/mknod.2.html
9+
// makedev, major and minor: https://man7.org/linux/man-pages/man3/makedev.3.html
10+
internal static partial class Sys
11+
{
12+
internal static int CreateBlockDevice(string pathName, uint mode, uint major, uint minor)
13+
{
14+
return MkNod(pathName, mode | FileTypes.S_IFBLK, major, minor);
15+
}
16+
17+
internal static int CreateCharacterDevice(string pathName, uint mode, uint major, uint minor)
18+
{
19+
return MkNod(pathName, mode | FileTypes.S_IFCHR, major, minor);
20+
}
21+
22+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkNod", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
23+
private static partial int MkNod(string pathName, uint mode, uint major, uint minor);
24+
25+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDeviceIdentifiers", SetLastError = true)]
26+
internal static unsafe partial int GetDeviceIdentifiers(ulong dev, uint* majorNumber, uint* minorNumber);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
6+
internal static partial class Interop
7+
{
8+
// mkfifo: https://man7.org/linux/man-pages/man3/mkfifo.3.html
9+
internal static partial class Sys
10+
{
11+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkFifo", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
12+
internal static partial int MkFifo(string pathName, uint mode);
13+
}
14+
}

src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ internal static class FileTypes
4141
internal const int S_IFIFO = 0x1000;
4242
internal const int S_IFCHR = 0x2000;
4343
internal const int S_IFDIR = 0x4000;
44+
internal const int S_IFBLK = 0x6000;
4445
internal const int S_IFREG = 0x8000;
4546
internal const int S_IFLNK = 0xA000;
4647
internal const int S_IFSOCK = 0xC000;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Runtime.InteropServices;
7+
8+
internal static partial class Interop
9+
{
10+
internal static partial class Kernel32
11+
{
12+
internal static void CreateHardLink(string hardLinkFilePath, string targetFilePath)
13+
{
14+
string originalPath = hardLinkFilePath;
15+
hardLinkFilePath = PathInternal.EnsureExtendedPrefix(hardLinkFilePath);
16+
targetFilePath = PathInternal.EnsureExtendedPrefix(targetFilePath);
17+
18+
if (!CreateHardLinkPrivate(hardLinkFilePath, targetFilePath, IntPtr.Zero))
19+
{
20+
throw Win32Marshal.GetExceptionForLastWin32Error(originalPath);
21+
}
22+
}
23+
24+
[LibraryImport(Libraries.Kernel32, EntryPoint = "CreateHardLinkW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
25+
[return: MarshalAs(UnmanagedType.Bool)]
26+
private static partial bool CreateHardLinkPrivate(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.IO
5+
{
6+
internal static partial class ArchivingUtils
7+
{
8+
internal static string SanitizeEntryFilePath(string entryPath) => entryPath.Replace('\0', '_');
9+
}
10+
}
+6-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
54
using System.Text;
65

7-
namespace System.IO.Compression
6+
namespace System.IO
87
{
9-
public static partial class ZipFileExtensions
8+
internal static partial class ArchivingUtils
109
{
11-
internal static string SanitizeZipFilePath(string zipPath)
10+
internal static string SanitizeEntryFilePath(string entryPath)
1211
{
13-
StringBuilder builder = new StringBuilder(zipPath);
14-
for (int i = 0; i < zipPath.Length; i++)
12+
StringBuilder builder = new StringBuilder(entryPath);
13+
for (int i = 0; i < entryPath.Length; i++)
1514
{
1615
if (((int)builder[i] >= 0 && (int)builder[i] < 32) ||
1716
builder[i] == '?' || builder[i] == ':' ||

src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Utils.cs src/libraries/Common/src/System/IO/Archiving.Utils.cs

+17-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77

8-
namespace System.IO.Compression
8+
namespace System.IO
99
{
10-
internal static partial class ZipFileUtils
10+
internal static partial class ArchivingUtils
1111
{
12-
// Per the .ZIP File Format Specification 4.4.17.1 all slashes should be forward slashes
12+
// To ensure tar files remain compatible with Unix,
13+
// and per the ZIP File Format Specification 4.4.17.1,
14+
// all slashes should be forward slashes.
1315
private const char PathSeparatorChar = '/';
1416
private const string PathSeparatorString = "/";
1517

@@ -74,5 +76,17 @@ public static bool IsDirEmpty(DirectoryInfo possiblyEmptyDir)
7476
using (IEnumerator<string> enumerator = Directory.EnumerateFileSystemEntries(possiblyEmptyDir.FullName).GetEnumerator())
7577
return !enumerator.MoveNext();
7678
}
79+
80+
public static void AttemptSetLastWriteTime(string destinationFileName, DateTimeOffset lastWriteTime)
81+
{
82+
try
83+
{
84+
File.SetLastWriteTime(destinationFileName, lastWriteTime.DateTime);
85+
}
86+
catch (UnauthorizedAccessException)
87+
{
88+
// Some OSes like Android (#35374) might not support setting the last write time, the extraction should not fail because of that
89+
}
90+
}
7791
}
7892
}

src/libraries/Common/tests/Resources/Strings.resx

+4-1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
<data name="Argument_InvalidPathChars" xml:space="preserve">
121121
<value>Argument_InvalidPathChars {0}</value>
122122
</data>
123+
<data name="ArgumentOutOfRange_FileLengthTooBig" xml:space="preserve">
124+
<value>Specified file length was too large for the file system.</value>
125+
</data>
123126
<data name="IO_FileNotFound" xml:space="preserve">
124127
<value>IO_FileNotFound</value>
125128
</data>
@@ -201,4 +204,4 @@
201204
<data name="net_quic_streamaborted" xml:space="preserve">
202205
<value>Stream aborted by peer ({0}).</value>
203206
</data>
204-
</root>
207+
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.1.32119.435
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9BE8AFF4-D37B-49AF-AFD3-A15E514AC8AE}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{55A8C7E4-925C-4F21-B68B-CEFC19137A4B}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6CF0D830-3EE9-44B1-B548-EA8750AD7B3E}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Tar", "ref\System.Formats.Tar.csproj", "{E0B882C6-2082-45F2-806E-568461A61975}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Tar", "src\System.Formats.Tar.csproj", "{9F751C2B-56DD-4604-A3F3-568627F8C006}"
15+
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Tar.Tests", "tests\System.Formats.Tar.Tests.csproj", "{6FD1E284-7B50-4077-B73A-5B31CB0E3577}"
17+
EndProject
18+
Global
19+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
20+
Debug|Any CPU = Debug|Any CPU
21+
Release|Any CPU = Release|Any CPU
22+
EndGlobalSection
23+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24+
{E0B882C6-2082-45F2-806E-568461A61975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{E0B882C6-2082-45F2-806E-568461A61975}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{E0B882C6-2082-45F2-806E-568461A61975}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{E0B882C6-2082-45F2-806E-568461A61975}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{9F751C2B-56DD-4604-A3F3-568627F8C006}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{9F751C2B-56DD-4604-A3F3-568627F8C006}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{9F751C2B-56DD-4604-A3F3-568627F8C006}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{9F751C2B-56DD-4604-A3F3-568627F8C006}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{6FD1E284-7B50-4077-B73A-5B31CB0E3577}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{6FD1E284-7B50-4077-B73A-5B31CB0E3577}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{6FD1E284-7B50-4077-B73A-5B31CB0E3577}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{6FD1E284-7B50-4077-B73A-5B31CB0E3577}.Release|Any CPU.Build.0 = Release|Any CPU
36+
EndGlobalSection
37+
GlobalSection(SolutionProperties) = preSolution
38+
HideSolutionNode = FALSE
39+
EndGlobalSection
40+
GlobalSection(NestedProjects) = preSolution
41+
{E0B882C6-2082-45F2-806E-568461A61975} = {9BE8AFF4-D37B-49AF-AFD3-A15E514AC8AE}
42+
{9F751C2B-56DD-4604-A3F3-568627F8C006} = {55A8C7E4-925C-4F21-B68B-CEFC19137A4B}
43+
{6FD1E284-7B50-4077-B73A-5B31CB0E3577} = {6CF0D830-3EE9-44B1-B548-EA8750AD7B3E}
44+
EndGlobalSection
45+
GlobalSection(ExtensibilityGlobals) = postSolution
46+
SolutionGuid = {F9B8DA67-C83B-466D-907C-9541CDBDCFEF}
47+
EndGlobalSection
48+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// ------------------------------------------------------------------------------
4+
// Changes to this file must follow the https://aka.ms/api-review process.
5+
// ------------------------------------------------------------------------------
6+
7+
namespace System.Formats.Tar
8+
{
9+
public sealed partial class GnuTarEntry : System.Formats.Tar.PosixTarEntry
10+
{
11+
public GnuTarEntry(System.Formats.Tar.TarEntryType entryType, string entryName) { }
12+
public System.DateTimeOffset AccessTime { get { throw null; } set { } }
13+
public System.DateTimeOffset ChangeTime { get { throw null; } set { } }
14+
}
15+
public sealed partial class PaxTarEntry : System.Formats.Tar.PosixTarEntry
16+
{
17+
public PaxTarEntry(System.Formats.Tar.TarEntryType entryType, string entryName) { }
18+
public PaxTarEntry(System.Formats.Tar.TarEntryType entryType, string entryName, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string>> extendedAttributes) { }
19+
public System.Collections.Generic.IReadOnlyDictionary<string, string> ExtendedAttributes { get { throw null; } }
20+
}
21+
public abstract partial class PosixTarEntry : System.Formats.Tar.TarEntry
22+
{
23+
internal PosixTarEntry() { }
24+
public int DeviceMajor { get { throw null; } set { } }
25+
public int DeviceMinor { get { throw null; } set { } }
26+
public string GroupName { get { throw null; } set { } }
27+
public string UserName { get { throw null; } set { } }
28+
}
29+
public abstract partial class TarEntry
30+
{
31+
internal TarEntry() { }
32+
public int Checksum { get { throw null; } }
33+
public System.IO.Stream? DataStream { get { throw null; } set { } }
34+
public System.Formats.Tar.TarEntryType EntryType { get { throw null; } }
35+
public int Gid { get { throw null; } set { } }
36+
public long Length { get { throw null; } }
37+
public string LinkName { get { throw null; } set { } }
38+
public System.Formats.Tar.TarFileMode Mode { get { throw null; } set { } }
39+
public System.DateTimeOffset ModificationTime { get { throw null; } set { } }
40+
public string Name { get { throw null; } set { } }
41+
public int Uid { get { throw null; } set { } }
42+
public void ExtractToFile(string destinationFileName, bool overwrite) { }
43+
public override string ToString() { throw null; }
44+
}
45+
public enum TarEntryType : byte
46+
{
47+
V7RegularFile = (byte)0,
48+
RegularFile = (byte)48,
49+
HardLink = (byte)49,
50+
SymbolicLink = (byte)50,
51+
CharacterDevice = (byte)51,
52+
BlockDevice = (byte)52,
53+
Directory = (byte)53,
54+
Fifo = (byte)54,
55+
ContiguousFile = (byte)55,
56+
DirectoryList = (byte)68,
57+
LongLink = (byte)75,
58+
LongPath = (byte)76,
59+
MultiVolume = (byte)77,
60+
RenamedOrSymlinked = (byte)78,
61+
SparseFile = (byte)83,
62+
TapeVolume = (byte)86,
63+
GlobalExtendedAttributes = (byte)103,
64+
ExtendedAttributes = (byte)120,
65+
}
66+
public static partial class TarFile
67+
{
68+
public static void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination, bool includeBaseDirectory) { }
69+
public static void CreateFromDirectory(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory) { }
70+
public static void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName, bool overwriteFiles) { }
71+
public static void ExtractToDirectory(string sourceFileName, string destinationDirectoryName, bool overwriteFiles) { }
72+
}
73+
[System.FlagsAttribute]
74+
public enum TarFileMode
75+
{
76+
None = 0,
77+
OtherExecute = 1,
78+
OtherWrite = 2,
79+
OtherRead = 4,
80+
GroupExecute = 8,
81+
GroupWrite = 16,
82+
GroupRead = 32,
83+
UserExecute = 64,
84+
UserWrite = 128,
85+
UserRead = 256,
86+
StickyBit = 512,
87+
GroupSpecial = 1024,
88+
UserSpecial = 2048,
89+
}
90+
public enum TarFormat
91+
{
92+
Unknown = 0,
93+
V7 = 1,
94+
Ustar = 2,
95+
Pax = 3,
96+
Gnu = 4,
97+
}
98+
public sealed partial class TarReader : System.IDisposable
99+
{
100+
public TarReader(System.IO.Stream archiveStream, bool leaveOpen = false) { }
101+
public System.Formats.Tar.TarFormat Format { get { throw null; } }
102+
public System.Collections.Generic.IReadOnlyDictionary<string, string>? GlobalExtendedAttributes { get { throw null; } }
103+
public void Dispose() { }
104+
public System.Formats.Tar.TarEntry? GetNextEntry(bool copyData = false) { throw null; }
105+
}
106+
public sealed partial class TarWriter : System.IDisposable
107+
{
108+
public TarWriter(System.IO.Stream archiveStream, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string>>? globalExtendedAttributes = null, bool leaveOpen = false) { }
109+
public TarWriter(System.IO.Stream archiveStream, System.Formats.Tar.TarFormat archiveFormat, bool leaveOpen = false) { }
110+
public System.Formats.Tar.TarFormat Format { get { throw null; } }
111+
public void Dispose() { }
112+
public void WriteEntry(System.Formats.Tar.TarEntry entry) { }
113+
public void WriteEntry(string fileName, string? entryName) { }
114+
}
115+
public sealed partial class UstarTarEntry : System.Formats.Tar.PosixTarEntry
116+
{
117+
public UstarTarEntry(System.Formats.Tar.TarEntryType entryType, string entryName) { }
118+
}
119+
public sealed partial class V7TarEntry : System.Formats.Tar.TarEntry
120+
{
121+
public V7TarEntry(System.Formats.Tar.TarEntryType entryType, string entryName) { }
122+
}
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
4+
<Nullable>enable</Nullable>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="System.Formats.Tar.cs" />
8+
</ItemGroup>
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
11+
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections\ref\System.Collections.csproj" />
12+
</ItemGroup>
13+
</Project>

0 commit comments

Comments
 (0)