Solution restructured to use multiple test projects
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
build
|
build
|
||||||
coverage.json
|
coverage.json
|
||||||
|
coverage.cobertura.xml
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|||||||
@@ -17,22 +17,23 @@ build-debug:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG == null
|
- if: $CI_COMMIT_TAG == null
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet restore --no-cache --force
|
||||||
- dotnet build -c Debug --nologo --no-restore --no-incremental
|
- dotnet build -c Debug --nologo --no-restore --no-incremental
|
||||||
- mkdir ./artifacts
|
- mkdir ./artifacts
|
||||||
- mv ./AMWD.Common/bin/Debug/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common/bin/Debug/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common/bin/Debug/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common/bin/Debug/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.MessagePack/bin/Debug/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.MessagePack/bin/Debug/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.MessagePack/bin/Debug/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.MessagePack/bin/Debug/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- artifacts/*.nupkg
|
- artifacts/*.nupkg
|
||||||
@@ -46,15 +47,24 @@ test-debug:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG == null
|
- if: $CI_COMMIT_TAG == null
|
||||||
# line-coverage
|
#coverage: /Line coverage[\s\S].+%/
|
||||||
#coverage: '/Total[^|]*\|\s*([0-9.%]+)/'
|
coverage: /Branch coverage[\s\S].+%/
|
||||||
# branch-coverage
|
before_script:
|
||||||
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet restore --no-cache --force
|
||||||
- dotnet test -c Debug --nologo --no-restore
|
- dotnet test -c Debug --nologo --no-restore /p:CoverletOutputFormat=Cobertura
|
||||||
|
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||||
|
- cat /reports/Summary.txt
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: ./**/coverage.cobertura.xml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -63,22 +73,23 @@ build-release:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG != null
|
- if: $CI_COMMIT_TAG != null
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet restore --no-cache --force
|
||||||
- dotnet build -c Release --nologo --no-restore --no-incremental
|
- dotnet build -c Release --nologo --no-restore --no-incremental
|
||||||
- mkdir ./artifacts
|
- mkdir ./artifacts
|
||||||
- mv ./AMWD.Common/bin/Release/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common/bin/Release/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common/bin/Release/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common/bin/Release/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.MessagePack/bin/Release/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.MessagePack/bin/Release/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.MessagePack/bin/Release/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.MessagePack/bin/Release/*.snupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/
|
- mv ./src/AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/
|
||||||
- mv ./AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/
|
- mv ./src/AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- artifacts/*.nupkg
|
- artifacts/*.nupkg
|
||||||
@@ -92,15 +103,24 @@ test-release:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG != null
|
- if: $CI_COMMIT_TAG != null
|
||||||
# line-coverage
|
#coverage: /Line coverage[\s\S].+%/
|
||||||
#coverage: '/Total[^|]*\|\s*([0-9.%]+)/'
|
coverage: /Branch coverage[\s\S].+%/
|
||||||
# branch-coverage
|
before_script:
|
||||||
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet restore --no-cache --force
|
||||||
- dotnet test -c Release --nologo --no-restore
|
- dotnet test -c Release --nologo --no-restore /p:CoverletOutputFormat=Cobertura
|
||||||
|
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||||
|
- cat /reports/Summary.txt
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: ./**/coverage.cobertura.xml
|
||||||
|
|
||||||
deploy-common:
|
deploy-common:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
@@ -110,6 +130,7 @@ deploy-common:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG =~ /^v[0-9.]+/
|
- if: $CI_COMMIT_TAG =~ /^v[0-9.]+/
|
||||||
script:
|
script:
|
||||||
@@ -123,12 +144,13 @@ deploy-aspnet:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG =~ /^asp\/v[0-9.]+/
|
- if: $CI_COMMIT_TAG =~ /^asp\/v[0-9.]+/
|
||||||
script:
|
script:
|
||||||
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.AspNetCore.*.nupkg
|
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.AspNetCore.*.nupkg
|
||||||
|
|
||||||
deploy-entityframework:
|
deploy-efcore:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-release
|
- build-release
|
||||||
@@ -136,12 +158,13 @@ deploy-entityframework:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/
|
- if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/
|
||||||
script:
|
script:
|
||||||
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.EntityFrameworkCore.*.nupkg
|
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.EntityFrameworkCore.*.nupkg
|
||||||
|
|
||||||
deploy-messagepack:
|
deploy-msgpack:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-release
|
- build-release
|
||||||
@@ -149,6 +172,7 @@ deploy-messagepack:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG =~ /^msgpack\/v[0-9.]+/
|
- if: $CI_COMMIT_TAG =~ /^msgpack\/v[0-9.]+/
|
||||||
script:
|
script:
|
||||||
@@ -162,6 +186,7 @@ deploy-test:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- lnx
|
- lnx
|
||||||
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG =~ /^test\/v[0-9.]+/
|
- if: $CI_COMMIT_TAG =~ /^test\/v[0-9.]+/
|
||||||
script:
|
script:
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("UnitTests")]
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace AMWD.Common.Packing.Tar.Interfaces
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface of a archive writer.
|
|
||||||
/// </summary>
|
|
||||||
public interface IArchiveDataWriter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Write <paramref name="count"/> bytes of data from <paramref name="buffer"/> to corresponding archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The data storage.</param>
|
|
||||||
/// <param name="count">How many bytes to be written to the corresponding archive.</param>
|
|
||||||
int Write(byte[] buffer, int count);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the writer can write.
|
|
||||||
/// </summary>
|
|
||||||
bool CanWrite { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The writer delegate.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The writer.</param>
|
|
||||||
public delegate void WriteDataDelegate(IArchiveDataWriter writer);
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
using System;
|
|
||||||
using AMWD.Common.Packing.Tar.Utils;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Interfaces
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// See "struct star_header" in <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html" />
|
|
||||||
/// </summary>
|
|
||||||
public interface ITarHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name field is the file name of the file, with directory names (if any) preceding the file name,
|
|
||||||
/// separated by slashes.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>name</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>0</c>
|
|
||||||
/// </remarks>
|
|
||||||
string FileName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The mode field provides nine bits specifying file permissions and three bits to specify
|
|
||||||
/// the Set UID, Set GID, and Save Text (sticky) modes.
|
|
||||||
/// When special permissions are required to create a file with a given mode,
|
|
||||||
/// and the user restoring files from the archive does not hold such permissions,
|
|
||||||
/// the mode bit(s) specifying those special permissions are ignored.
|
|
||||||
/// Modes which are not supported by the operating system restoring files from the archive will be ignored.
|
|
||||||
/// Unsupported modes should be faked up when creating or updating an archive; e.g.,
|
|
||||||
/// the group permission could be copied from the other permission.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>mode</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>100</c>
|
|
||||||
/// </remarks>
|
|
||||||
int Mode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The uid field is the numeric user ID of the file owners.
|
|
||||||
/// If the operating system does not support numeric user ID, this field should be ignored.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>uid</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>108</c>
|
|
||||||
/// </remarks>
|
|
||||||
int UserId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The gid fields is the numeric group ID of the file owners.
|
|
||||||
/// If the operating system does not support numeric group ID, this field should be ignored.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>gid</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>116</c>
|
|
||||||
/// </remarks>
|
|
||||||
int GroupId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size field is the size of the file in bytes;
|
|
||||||
/// linked files are archived with this field specified as zero.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>size</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>124</c>
|
|
||||||
/// </remarks>
|
|
||||||
long SizeInBytes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>mtime</para>
|
|
||||||
/// <para>byte offset: 136</para>
|
|
||||||
/// The mtime field represents the data modification time of the file at the time it was archived.
|
|
||||||
/// It represents the integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>mtime</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>136</c>
|
|
||||||
/// </remarks>
|
|
||||||
DateTime LastModification { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The typeflag field specifies the type of file archived.
|
|
||||||
/// If a particular implementation does not recognize or permit the specified type,
|
|
||||||
/// the file will be extracted as if it were a regular file.
|
|
||||||
/// As this action occurs, tar issues a warning to the standard error.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>typeflag</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>156</c>
|
|
||||||
/// </remarks>
|
|
||||||
EntryType EntryType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The uname field will contain the ASCII representation of the owner of the file.
|
|
||||||
/// If found, the user ID is used rather than the value in the uid field.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>uname</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>265</c>
|
|
||||||
/// </remarks>
|
|
||||||
string UserName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The gname field will contain the ASCII representation of the group of the file.
|
|
||||||
/// If found, the group ID is used rather than the values in the gid field.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>gname</c>
|
|
||||||
/// <br/>
|
|
||||||
/// Byte offset: <c>297</c>
|
|
||||||
/// </remarks>
|
|
||||||
string GroupName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of this header.
|
|
||||||
/// </summary>
|
|
||||||
int HeaderSize { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using AMWD.Common.Packing.Tar.Interfaces;
|
|
||||||
using AMWD.Common.Packing.Tar.Utils;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Extract contents of a tar file represented by a stream for the TarReader constructor.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Constructs TarReader object to read data from `tarredData` stream.
|
|
||||||
/// <br />
|
|
||||||
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarReader.cs">DotnetMakeDeb</see>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="tarredData">A stream to read tar archive from</param>
|
|
||||||
public class TarReader(Stream tarredData)
|
|
||||||
{
|
|
||||||
private readonly byte[] _dataBuffer = new byte[512];
|
|
||||||
private readonly UsTarHeader _header = new();
|
|
||||||
private readonly Stream _inStream = tarredData;
|
|
||||||
private long _remainingBytesInFile;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file info (the header).
|
|
||||||
/// </summary>
|
|
||||||
public ITarHeader FileInfo => _header;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read all files from an archive to a directory. It creates some child directories to
|
|
||||||
/// reproduce a file structure from the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="destDirectory">The out directory.</param>
|
|
||||||
///
|
|
||||||
/// CAUTION! This method is not safe. It's not tar-bomb proof.
|
|
||||||
/// {see http://en.wikipedia.org/wiki/Tar_(file_format) }
|
|
||||||
/// If you are not sure about the source of an archive you extracting,
|
|
||||||
/// then use MoveNext and Read and handle paths like ".." and "../.." according
|
|
||||||
/// to your business logic.
|
|
||||||
public void ReadToEnd(string destDirectory)
|
|
||||||
{
|
|
||||||
while (MoveNext(skipData: false))
|
|
||||||
{
|
|
||||||
string fileNameFromArchive = FileInfo.FileName;
|
|
||||||
string totalPath = destDirectory + Path.DirectorySeparatorChar + fileNameFromArchive;
|
|
||||||
if (UsTarHeader.IsPathSeparator(fileNameFromArchive[fileNameFromArchive.Length - 1]) || FileInfo.EntryType == EntryType.Directory)
|
|
||||||
{
|
|
||||||
// Record is a directory
|
|
||||||
Directory.CreateDirectory(totalPath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If record is a file
|
|
||||||
string fileName = Path.GetFileName(totalPath);
|
|
||||||
string directory = totalPath.Remove(totalPath.Length - fileName.Length);
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
|
|
||||||
using FileStream file = File.Create(totalPath);
|
|
||||||
Read(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read data from the current archive to a Stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataDestanation">A stream to read data to</param>
|
|
||||||
/// <seealso cref="MoveNext"/>
|
|
||||||
public void Read(Stream dataDestanation)
|
|
||||||
{
|
|
||||||
Debug.WriteLine("tar stream position Read in: " + _inStream.Position);
|
|
||||||
|
|
||||||
int readBytes;
|
|
||||||
while ((readBytes = Read(out byte[] read)) != -1)
|
|
||||||
{
|
|
||||||
Debug.WriteLine("tar stream position Read while(...) : " + _inStream.Position);
|
|
||||||
dataDestanation.Write(read, 0, readBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.WriteLine("tar stream position Read out: " + _inStream.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads data from the current archive to a buffer array.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The buffer array.</param>
|
|
||||||
/// <returns>The nuber of bytes read.</returns>
|
|
||||||
protected int Read(out byte[] buffer)
|
|
||||||
{
|
|
||||||
if (_remainingBytesInFile == 0)
|
|
||||||
{
|
|
||||||
buffer = null;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int align512 = -1;
|
|
||||||
long toRead = _remainingBytesInFile - 512;
|
|
||||||
|
|
||||||
if (toRead > 0)
|
|
||||||
{
|
|
||||||
toRead = 512;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
align512 = 512 - (int)_remainingBytesInFile;
|
|
||||||
toRead = _remainingBytesInFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesRead = _inStream.Read(_dataBuffer, 0, (int)toRead);
|
|
||||||
_remainingBytesInFile -= bytesRead;
|
|
||||||
|
|
||||||
if (_inStream.CanSeek && align512 > 0)
|
|
||||||
{
|
|
||||||
_inStream.Seek(align512, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
while (align512 > 0)
|
|
||||||
{
|
|
||||||
_inStream.ReadByte();
|
|
||||||
--align512;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = _dataBuffer;
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if all bytes in buffer are zeroes
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">buffer to check</param>
|
|
||||||
/// <returns>true if all bytes are zeroes, otherwise false</returns>
|
|
||||||
private static bool IsEmpty(IEnumerable<byte> buffer)
|
|
||||||
{
|
|
||||||
foreach (byte b in buffer)
|
|
||||||
{
|
|
||||||
if (b != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Move internal pointer to a next file in archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="skipData">Should be true if you want to read a header only, otherwise false</param>
|
|
||||||
/// <returns>false on End Of File otherwise true</returns>
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// while(MoveNext())
|
|
||||||
/// {
|
|
||||||
/// Read(dataDestStream);
|
|
||||||
/// }
|
|
||||||
/// <seealso cref="Read(Stream)"/>
|
|
||||||
public bool MoveNext(bool skipData)
|
|
||||||
{
|
|
||||||
Debug.WriteLine("tar stream position MoveNext in: " + _inStream.Position);
|
|
||||||
if (_remainingBytesInFile > 0)
|
|
||||||
{
|
|
||||||
if (!skipData)
|
|
||||||
{
|
|
||||||
throw new TarException(
|
|
||||||
"You are trying to change file while not all the data from the previous one was read. If you do want to skip files use skipData parameter set to true.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip to the end of file.
|
|
||||||
if (_inStream.CanSeek)
|
|
||||||
{
|
|
||||||
long remainer = _remainingBytesInFile % 512;
|
|
||||||
_inStream.Seek(_remainingBytesInFile + (512 - (remainer == 0 ? 512 : remainer)), SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (Read(out _) != -1) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = _header.GetBytes();
|
|
||||||
|
|
||||||
int headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
|
|
||||||
if (headerRead < 512)
|
|
||||||
throw new TarException("Can not read header");
|
|
||||||
|
|
||||||
if (IsEmpty(bytes))
|
|
||||||
{
|
|
||||||
headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
|
|
||||||
if (headerRead == 512 && IsEmpty(bytes))
|
|
||||||
{
|
|
||||||
Debug.WriteLine("tar stream position MoveNext out(false): " + _inStream.Position);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw new TarException("Broken archive");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_header.UpdateHeaderFromBytes())
|
|
||||||
{
|
|
||||||
throw new TarException("Checksum check failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
_remainingBytesInFile = _header.SizeInBytes;
|
|
||||||
|
|
||||||
Debug.WriteLine("tar stream position MoveNext out(true): " + _inStream.Position);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using AMWD.Common.Packing.Tar.Interfaces;
|
|
||||||
using AMWD.Common.Packing.Tar.Utils;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a tar (see GNU tar) archive to a stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs">DotnetMakeDeb</see>
|
|
||||||
/// </remarks>
|
|
||||||
public class TarWriter : LegacyTarWriter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initilizes a new instance of the <see cref="TarWriter"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="outStream">The stream to write the archive to.</param>
|
|
||||||
public TarWriter(Stream outStream)
|
|
||||||
: base(outStream)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes an entry header (file, dir, ...) to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification time.</param>
|
|
||||||
/// <param name="count">The number of bytes.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="entryType">The entry type.</param>
|
|
||||||
protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
|
|
||||||
{
|
|
||||||
var tarHeader = new UsTarHeader()
|
|
||||||
{
|
|
||||||
FileName = name,
|
|
||||||
Mode = mode,
|
|
||||||
UserId = userId,
|
|
||||||
GroupId = groupId,
|
|
||||||
SizeInBytes = count,
|
|
||||||
LastModification = lastModificationTime,
|
|
||||||
EntryType = entryType,
|
|
||||||
UserName = Convert.ToString(userId, 8),
|
|
||||||
GroupName = Convert.ToString(groupId, 8)
|
|
||||||
};
|
|
||||||
OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes an entry header (file, dir, ...) to the archive.
|
|
||||||
/// Hashes the username and groupname down to a HashCode.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification time.</param>
|
|
||||||
/// <param name="count">The number of bytes.</param>
|
|
||||||
/// <param name="userName">The username.</param>
|
|
||||||
/// <param name="groupName">The group name.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="entryType">The entry type.</param>
|
|
||||||
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode, EntryType entryType)
|
|
||||||
{
|
|
||||||
WriteHeader(
|
|
||||||
name: name,
|
|
||||||
lastModificationTime: lastModificationTime,
|
|
||||||
count: count,
|
|
||||||
userId: userName.GetHashCode(),
|
|
||||||
groupId: groupName.GetHashCode(),
|
|
||||||
mode: mode,
|
|
||||||
entryType: entryType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a file to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The file name.</param>
|
|
||||||
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
|
|
||||||
/// <param name="userName">The username.</param>
|
|
||||||
/// <param name="groupName">The group name.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification time.</param>
|
|
||||||
/// <param name="writeDelegate">The write handle.</param>
|
|
||||||
public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
|
|
||||||
{
|
|
||||||
var writer = new DataWriter(OutStream, dataSizeInBytes);
|
|
||||||
WriteHeader(name, lastModificationTime, dataSizeInBytes, userName, groupName, mode, EntryType.File);
|
|
||||||
while (writer.CanWrite)
|
|
||||||
{
|
|
||||||
writeDelegate(writer);
|
|
||||||
}
|
|
||||||
AlignTo512(dataSizeInBytes, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a file to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The file stream to add to the archive.</param>
|
|
||||||
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
|
|
||||||
/// <param name="fileName">The file name.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification time.</param>
|
|
||||||
public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode,
|
|
||||||
DateTime lastModificationTime)
|
|
||||||
{
|
|
||||||
WriteHeader(fileName, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
|
|
||||||
WriteContent(dataSizeInBytes, data);
|
|
||||||
AlignTo512(dataSizeInBytes, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using AMWD.Common.Packing.Tar.Interfaces;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
internal class DataWriter : IArchiveDataWriter
|
|
||||||
{
|
|
||||||
private readonly long _size;
|
|
||||||
private long _remainingBytes;
|
|
||||||
private readonly Stream _stream;
|
|
||||||
|
|
||||||
public DataWriter(Stream data, long dataSizeInBytes)
|
|
||||||
{
|
|
||||||
_size = dataSizeInBytes;
|
|
||||||
_remainingBytes = _size;
|
|
||||||
_stream = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanWrite { get; private set; } = true;
|
|
||||||
|
|
||||||
public int Write(byte[] buffer, int count)
|
|
||||||
{
|
|
||||||
if (_remainingBytes == 0)
|
|
||||||
{
|
|
||||||
CanWrite = false;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesToWrite;
|
|
||||||
if (_remainingBytes - count < 0)
|
|
||||||
{
|
|
||||||
bytesToWrite = (int)_remainingBytes;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bytesToWrite = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream.Write(buffer, 0, bytesToWrite);
|
|
||||||
_remainingBytes -= bytesToWrite;
|
|
||||||
|
|
||||||
return bytesToWrite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// See "Values used in typeflag field." in <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html" />
|
|
||||||
/// </summary>
|
|
||||||
public enum EntryType : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// AREGTYPE, regular file
|
|
||||||
/// </summary>
|
|
||||||
File = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// REGTYPE, regular file
|
|
||||||
/// </summary>
|
|
||||||
FileObsolete = 0x30,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// LNKTYPE, link
|
|
||||||
/// </summary>
|
|
||||||
HardLink = 0x31,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SYMTYPE, reserved
|
|
||||||
/// </summary>
|
|
||||||
SymLink = 0x32,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CHRTYPE, character special
|
|
||||||
/// </summary>
|
|
||||||
CharDevice = 0x33,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BLKTYPE, block special
|
|
||||||
/// </summary>
|
|
||||||
BlockDevice = 0x34,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DIRTYPE, directory
|
|
||||||
/// </summary>
|
|
||||||
Directory = 0x35,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// FIFOTYPE, FIFO special
|
|
||||||
/// </summary>
|
|
||||||
Fifo = 0x36,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CONTTYPE, reserved
|
|
||||||
/// </summary>
|
|
||||||
Content = 0x37,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// XHDTYPE, Extended header referring to the next file in the archive
|
|
||||||
/// </summary>
|
|
||||||
ExtendedHeader = 0x78,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// XGLTYPE, Global extended header
|
|
||||||
/// </summary>
|
|
||||||
GlobalExtendedHeader = 0x67,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GNUTYPE_LONGLINK, Identifies the *next* file on the tape as having a long linkname.
|
|
||||||
/// </summary>
|
|
||||||
LongLink = 0x4b,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GNUTYPE_LONGNAME, Identifies the *next* file on the tape as having a long name.
|
|
||||||
/// </summary>
|
|
||||||
LongName = 0x4c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using AMWD.Common.Packing.Tar.Interfaces;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Implements a legacy TAR writer.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Writes tar (see GNU tar) archive to a stream
|
|
||||||
/// <br/>
|
|
||||||
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/LegacyTarWriter.cs">DotnetMakeDeb</see>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="outStream">stream to write archive to</param>
|
|
||||||
public class LegacyTarWriter(Stream outStream) : IDisposable
|
|
||||||
{
|
|
||||||
private readonly Stream _outStream = outStream;
|
|
||||||
private bool _isClosed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The buffer for writing.
|
|
||||||
/// </summary>
|
|
||||||
protected byte[] buffer = new byte[1024];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether to read on zero.
|
|
||||||
/// </summary>
|
|
||||||
public bool ReadOnZero { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the output stream.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual Stream OutStream
|
|
||||||
{
|
|
||||||
get { return _outStream; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable Members
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
=> Close();
|
|
||||||
|
|
||||||
#endregion IDisposable Members
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a directory entry.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path to the directory.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="path"/> is not set.</exception>
|
|
||||||
public void WriteDirectoryEntry(string path, int userId, int groupId, int mode)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
throw new ArgumentNullException(nameof(path), "The path is not set.");
|
|
||||||
if (path[path.Length - 1] != '/')
|
|
||||||
path += '/';
|
|
||||||
|
|
||||||
DateTime lastWriteTime;
|
|
||||||
if (Directory.Exists(path))
|
|
||||||
{
|
|
||||||
lastWriteTime = Directory.GetLastWriteTime(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastWriteTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle long path names (> 99 characters)
|
|
||||||
if (path.Length > 99)
|
|
||||||
{
|
|
||||||
WriteLongName(
|
|
||||||
name: path,
|
|
||||||
userId: userId,
|
|
||||||
groupId: groupId,
|
|
||||||
mode: mode,
|
|
||||||
lastModificationTime: lastWriteTime);
|
|
||||||
|
|
||||||
// shorten the path name so it can be written properly
|
|
||||||
path = path.Substring(0, 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteHeader(path, lastWriteTime, 0, userId, groupId, mode, EntryType.Directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a directory and its contents.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="directory">The directory.</param>
|
|
||||||
/// <param name="doRecursive">Write also sub-directories.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="directory"/> is not set.</exception>
|
|
||||||
public void WriteDirectory(string directory, bool doRecursive)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(directory))
|
|
||||||
throw new ArgumentNullException(nameof(directory), "The directory is not set.");
|
|
||||||
|
|
||||||
WriteDirectoryEntry(directory, 0, 0, 0755);
|
|
||||||
|
|
||||||
string[] files = Directory.GetFiles(directory);
|
|
||||||
foreach (string fileName in files)
|
|
||||||
Write(fileName);
|
|
||||||
|
|
||||||
string[] directories = Directory.GetDirectories(directory);
|
|
||||||
foreach (string dirName in directories)
|
|
||||||
{
|
|
||||||
WriteDirectoryEntry(dirName, 0, 0, 0755);
|
|
||||||
if (doRecursive)
|
|
||||||
WriteDirectory(dirName, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The file.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="fileName"/> is not set.</exception>
|
|
||||||
public void Write(string fileName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
throw new ArgumentNullException(nameof(fileName), "The file name is not set.");
|
|
||||||
|
|
||||||
using var fileStream = File.OpenRead(fileName);
|
|
||||||
Write(fileStream, fileStream.Length, fileName, 61, 61, 511, File.GetLastWriteTime(fileStream.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a file stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">The file stream.</param>
|
|
||||||
public void Write(FileStream file)
|
|
||||||
{
|
|
||||||
string path = Path.GetFullPath(file.Name).Replace(Path.GetPathRoot(file.Name), string.Empty);
|
|
||||||
path = path.Replace(Path.DirectorySeparatorChar, '/');
|
|
||||||
Write(file, file.Length, path, 61, 61, 511, File.GetLastWriteTime(file.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The contents.</param>
|
|
||||||
/// <param name="dataSizeInBytes">The file size in bytes.</param>
|
|
||||||
/// <param name="name">The file name.</param>
|
|
||||||
public void Write(Stream data, long dataSizeInBytes, string name)
|
|
||||||
=> Write(data, dataSizeInBytes, name, 61, 61, 511, DateTime.Now);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a file to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The file name.</param>
|
|
||||||
/// <param name="dataSizeInBytes">The file size in bytes.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification timestamp.</param>
|
|
||||||
/// <param name="writeDelegate">The <see cref="WriteDataDelegate"/>.</param>
|
|
||||||
public virtual void Write(string name, long dataSizeInBytes, int userId, int groupId, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
|
|
||||||
{
|
|
||||||
var writer = new DataWriter(OutStream, dataSizeInBytes);
|
|
||||||
|
|
||||||
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
|
|
||||||
|
|
||||||
while (writer.CanWrite)
|
|
||||||
writeDelegate(writer);
|
|
||||||
|
|
||||||
AlignTo512(dataSizeInBytes, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a stream as file to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The content as <see cref="Stream"/>.</param>
|
|
||||||
/// <param name="dataSizeInBytes">The file size in bytes.</param>
|
|
||||||
/// <param name="name">The file name.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The access mode.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification timestamp.</param>
|
|
||||||
/// <exception cref="TarException">This writer is already closed.</exception>
|
|
||||||
public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, DateTime lastModificationTime)
|
|
||||||
{
|
|
||||||
if (_isClosed)
|
|
||||||
throw new TarException("Can not write to the closed writer");
|
|
||||||
|
|
||||||
// handle long file names (> 99 characters)
|
|
||||||
if (name.Length > 99)
|
|
||||||
{
|
|
||||||
WriteLongName(
|
|
||||||
name: name,
|
|
||||||
userId: userId,
|
|
||||||
groupId: groupId,
|
|
||||||
mode: mode,
|
|
||||||
lastModificationTime: lastModificationTime);
|
|
||||||
|
|
||||||
// shorten the file name so it can be written properly
|
|
||||||
name = name.Substring(0, 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
|
|
||||||
WriteContent(dataSizeInBytes, data);
|
|
||||||
AlignTo512(dataSizeInBytes, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle long file or path names (> 99 characters).
|
|
||||||
/// Write header and content, which its content contain the long (complete) file/path name.
|
|
||||||
/// <para>This handling method is adapted from https://github.com/qmfrederik/dotnet-packaging/pull/50/files#diff-f64c58cc18e8e445cee6ffed7a0d765cdb442c0ef21a3ed80bd20514057967b1 </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">File name or path name.</param>
|
|
||||||
/// <param name="userId">User ID.</param>
|
|
||||||
/// <param name="groupId">Group ID.</param>
|
|
||||||
/// <param name="mode">Mode.</param>
|
|
||||||
/// <param name="lastModificationTime">Last modification time.</param>
|
|
||||||
private void WriteLongName(string name, int userId, int groupId, int mode, DateTime lastModificationTime)
|
|
||||||
{
|
|
||||||
// must include a trailing \0
|
|
||||||
int nameLength = Encoding.UTF8.GetByteCount(name);
|
|
||||||
byte[] entryName = new byte[nameLength + 1];
|
|
||||||
|
|
||||||
Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0);
|
|
||||||
|
|
||||||
// add a "././@LongLink" pseudo-entry which contains the full name
|
|
||||||
using var nameStream = new MemoryStream(entryName);
|
|
||||||
WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName);
|
|
||||||
WriteContent(entryName.Length, nameStream);
|
|
||||||
AlignTo512(entryName.Length, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a stream as file to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="count">The size of the file in bytes.</param>
|
|
||||||
/// <param name="data">The file content as stream.</param>
|
|
||||||
/// <exception cref="IOException"><paramref name="data"/> has not enough to read from.</exception>
|
|
||||||
protected void WriteContent(long count, Stream data)
|
|
||||||
{
|
|
||||||
while (count > 0 && count > buffer.Length)
|
|
||||||
{
|
|
||||||
int bytesRead = data.Read(buffer, 0, buffer.Length);
|
|
||||||
if (bytesRead < 0)
|
|
||||||
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
|
|
||||||
|
|
||||||
if (bytesRead == 0)
|
|
||||||
{
|
|
||||||
if (ReadOnZero)
|
|
||||||
Thread.Sleep(100);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
OutStream.Write(buffer, 0, bytesRead);
|
|
||||||
count -= bytesRead;
|
|
||||||
}
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
int bytesRead = data.Read(buffer, 0, (int)count);
|
|
||||||
if (bytesRead < 0)
|
|
||||||
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
|
|
||||||
|
|
||||||
if (bytesRead == 0)
|
|
||||||
{
|
|
||||||
while (count > 0)
|
|
||||||
{
|
|
||||||
OutStream.WriteByte(0);
|
|
||||||
--count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
OutStream.Write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a entry header to the archive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The file name.</param>
|
|
||||||
/// <param name="lastModificationTime">The last modification time.</param>
|
|
||||||
/// <param name="count">The number of bytes.</param>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="groupId">The group id.</param>
|
|
||||||
/// <param name="mode">The file mode.</param>
|
|
||||||
/// <param name="entryType">The entry type</param>
|
|
||||||
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
|
|
||||||
{
|
|
||||||
var header = new TarHeader
|
|
||||||
{
|
|
||||||
FileName = name,
|
|
||||||
LastModification = lastModificationTime,
|
|
||||||
SizeInBytes = count,
|
|
||||||
UserId = userId,
|
|
||||||
GroupId = groupId,
|
|
||||||
Mode = mode,
|
|
||||||
EntryType = entryType
|
|
||||||
};
|
|
||||||
OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aligns the entry to 512 bytes.
|
|
||||||
/// </summary>
|
|
||||||
public void AlignTo512(long size, bool acceptZero)
|
|
||||||
{
|
|
||||||
size %= 512;
|
|
||||||
if (size == 0 && !acceptZero) return;
|
|
||||||
while (size < 512)
|
|
||||||
{
|
|
||||||
OutStream.WriteByte(0);
|
|
||||||
size++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the writer and aligns to 512 bytes.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Close()
|
|
||||||
{
|
|
||||||
if (_isClosed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AlignTo512(0, true);
|
|
||||||
AlignTo512(0, true);
|
|
||||||
|
|
||||||
_isClosed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents errors that occur during tar archive execution.
|
|
||||||
/// </summary>
|
|
||||||
public class TarException : Exception
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TarException"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public TarException()
|
|
||||||
: base()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TarException"/> class with a specified
|
|
||||||
/// error message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The message that describes the error.</param>
|
|
||||||
public TarException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the System.Exception class with a specified error
|
|
||||||
/// message and a reference to the inner exception that is the cause of this exception.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
|
||||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
|
||||||
/// if no inner exception is specified.</param>
|
|
||||||
public TarException(string message, Exception innerException)
|
|
||||||
: base(message, innerException)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
#if !NET8_0_OR_GREATER
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TarException"/> class with serialized data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/> that holds the serialized
|
|
||||||
/// object data about the exception being thrown.</param>
|
|
||||||
/// <param name="context">The <see cref="System.Runtime.Serialization.StreamingContext"/> that contains contextual information
|
|
||||||
/// about the source or destination.</param>
|
|
||||||
protected TarException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
|
|
||||||
: base(info, context)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using AMWD.Common.Packing.Tar.Interfaces;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
internal class TarHeader : ITarHeader
|
|
||||||
{
|
|
||||||
private static readonly byte[] _spaces = Encoding.ASCII.GetBytes(" ");
|
|
||||||
private readonly byte[] _buffer = new byte[512];
|
|
||||||
|
|
||||||
private string _fileName;
|
|
||||||
private long _headerChecksum;
|
|
||||||
|
|
||||||
public TarHeader()
|
|
||||||
{
|
|
||||||
// Default values
|
|
||||||
Mode = 511; // 0777 dec
|
|
||||||
UserId = 61; // 101 dec
|
|
||||||
GroupId = 61; // 101 dec
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntryType EntryType { get; set; }
|
|
||||||
|
|
||||||
public virtual string FileName
|
|
||||||
{
|
|
||||||
get => _fileName.Replace("\0", string.Empty);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value.Length > 100)
|
|
||||||
throw new TarException("A file name can not be more than 100 chars long");
|
|
||||||
|
|
||||||
_fileName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Mode { get; set; }
|
|
||||||
|
|
||||||
public string ModeString
|
|
||||||
=> Convert.ToString(Mode, 8).PadLeft(7, '0');
|
|
||||||
|
|
||||||
public int UserId { get; set; }
|
|
||||||
|
|
||||||
public virtual string UserName
|
|
||||||
{
|
|
||||||
get => UserId.ToString();
|
|
||||||
set => UserId = int.Parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string UserIdString
|
|
||||||
=> Convert.ToString(UserId, 8).PadLeft(7, '0');
|
|
||||||
|
|
||||||
public int GroupId { get; set; }
|
|
||||||
|
|
||||||
public virtual string GroupName
|
|
||||||
{
|
|
||||||
get => GroupId.ToString();
|
|
||||||
set => GroupId = int.Parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GroupIdString
|
|
||||||
=> Convert.ToString(GroupId, 8).PadLeft(7, '0');
|
|
||||||
|
|
||||||
public long SizeInBytes { get; set; }
|
|
||||||
|
|
||||||
public string SizeString
|
|
||||||
=> Convert.ToString(SizeInBytes, 8).PadLeft(11, '0');
|
|
||||||
|
|
||||||
public DateTime LastModification { get; set; }
|
|
||||||
|
|
||||||
public string LastModificationString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
long unixTime = ((DateTimeOffset)DateTime.SpecifyKind(LastModification, DateTimeKind.Utc)).ToUnixTimeSeconds();
|
|
||||||
return Convert.ToString(unixTime, 8).PadLeft(11, '0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string HeaderChecksumString
|
|
||||||
=> Convert.ToString(_headerChecksum, 8).PadLeft(6, '0');
|
|
||||||
|
|
||||||
public virtual int HeaderSize => 512;
|
|
||||||
|
|
||||||
public byte[] GetBytes() => _buffer.ToArray();
|
|
||||||
|
|
||||||
public virtual bool UpdateHeaderFromBytes()
|
|
||||||
{
|
|
||||||
FileName = Encoding.ASCII.GetString(_buffer, 0, 100);
|
|
||||||
|
|
||||||
// Thanks to Shasha Alperocivh. Trimming nulls.
|
|
||||||
Mode = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 100, 7).Trim(), 8);
|
|
||||||
UserId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 108, 7).Trim(), 8);
|
|
||||||
GroupId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 116, 7).Trim(), 8);
|
|
||||||
|
|
||||||
EntryType = (EntryType)_buffer[156];
|
|
||||||
|
|
||||||
if ((_buffer[124] & 0x80) == 0x80) // if size in binary
|
|
||||||
{
|
|
||||||
long sizeBigEndian = BitConverter.ToInt64(_buffer, 0x80);
|
|
||||||
SizeInBytes = IPAddress.NetworkToHostOrder(sizeBigEndian);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 124, 11), 8);
|
|
||||||
}
|
|
||||||
long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 136, 11), 8);
|
|
||||||
LastModification = DateTimeOffset.FromUnixTimeSeconds(unixTimeStamp).DateTime;
|
|
||||||
|
|
||||||
int storedChecksum = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 148, 6));
|
|
||||||
RecalculateChecksum(_buffer);
|
|
||||||
if (storedChecksum == _headerChecksum)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
RecalculateAltChecksum(_buffer);
|
|
||||||
return storedChecksum == _headerChecksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RecalculateAltChecksum(byte[] buf)
|
|
||||||
{
|
|
||||||
_spaces.CopyTo(buf, 148);
|
|
||||||
_headerChecksum = 0;
|
|
||||||
foreach (byte b in buf)
|
|
||||||
{
|
|
||||||
if ((b & 0x80) == 0x80)
|
|
||||||
{
|
|
||||||
_headerChecksum -= b ^ 0x80;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_headerChecksum += b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual byte[] GetHeaderValue()
|
|
||||||
{
|
|
||||||
// Clean old values
|
|
||||||
Array.Clear(_buffer, 0, _buffer.Length);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(FileName))
|
|
||||||
throw new TarException("FileName can not be empty.");
|
|
||||||
|
|
||||||
if (FileName.Length >= 100)
|
|
||||||
throw new TarException("FileName is too long. It must be less than 100 bytes.");
|
|
||||||
|
|
||||||
// Fill header
|
|
||||||
Encoding.ASCII.GetBytes(FileName.PadRight(100, '\0')).CopyTo(_buffer, 0);
|
|
||||||
Encoding.ASCII.GetBytes(ModeString).CopyTo(_buffer, 100);
|
|
||||||
Encoding.ASCII.GetBytes(UserIdString).CopyTo(_buffer, 108);
|
|
||||||
Encoding.ASCII.GetBytes(GroupIdString).CopyTo(_buffer, 116);
|
|
||||||
Encoding.ASCII.GetBytes(SizeString).CopyTo(_buffer, 124);
|
|
||||||
Encoding.ASCII.GetBytes(LastModificationString).CopyTo(_buffer, 136);
|
|
||||||
|
|
||||||
// buffer[156] = 20;
|
|
||||||
_buffer[156] = ((byte)EntryType);
|
|
||||||
|
|
||||||
RecalculateChecksum(_buffer);
|
|
||||||
|
|
||||||
// Write checksum
|
|
||||||
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(_buffer, 148);
|
|
||||||
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RecalculateChecksum(byte[] buf)
|
|
||||||
{
|
|
||||||
// Set default value for checksum. That is 8 spaces.
|
|
||||||
_spaces.CopyTo(buf, 148);
|
|
||||||
|
|
||||||
// Calculate checksum
|
|
||||||
_headerChecksum = 0;
|
|
||||||
foreach (byte b in buf)
|
|
||||||
_headerChecksum += b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace AMWD.Common.Packing.Tar.Utils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// UsTar header implementation.
|
|
||||||
/// </summary>
|
|
||||||
internal class UsTarHeader : TarHeader
|
|
||||||
{
|
|
||||||
private const string Magic = "ustar";
|
|
||||||
private const string Version = " ";
|
|
||||||
|
|
||||||
private string _groupName;
|
|
||||||
|
|
||||||
private string _namePrefix = string.Empty;
|
|
||||||
private string _userName;
|
|
||||||
|
|
||||||
public override string UserName
|
|
||||||
{
|
|
||||||
get => _userName.Replace("\0", string.Empty);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value.Length > 32)
|
|
||||||
throw new TarException("user name can not be longer than 32 chars");
|
|
||||||
|
|
||||||
_userName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string GroupName
|
|
||||||
{
|
|
||||||
get => _groupName.Replace("\0", string.Empty);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value.Length > 32)
|
|
||||||
throw new TarException("group name can not be longer than 32 chars");
|
|
||||||
|
|
||||||
_groupName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string FileName
|
|
||||||
{
|
|
||||||
get => _namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value.Length > 100)
|
|
||||||
{
|
|
||||||
if (value.Length > 255)
|
|
||||||
throw new TarException("UsTar fileName can not be longer thatn 255 chars");
|
|
||||||
|
|
||||||
int position = value.Length - 100;
|
|
||||||
|
|
||||||
// Find first path separator in the remaining 100 chars of the file name
|
|
||||||
while (!IsPathSeparator(value[position]))
|
|
||||||
{
|
|
||||||
++position;
|
|
||||||
if (position == value.Length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (position == value.Length)
|
|
||||||
position = value.Length - 100;
|
|
||||||
_namePrefix = value.Substring(0, position);
|
|
||||||
|
|
||||||
base.FileName = value.Substring(position, value.Length - position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base.FileName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool UpdateHeaderFromBytes()
|
|
||||||
{
|
|
||||||
byte[] bytes = GetBytes();
|
|
||||||
|
|
||||||
UserName = Encoding.ASCII.GetString(bytes, 0x109, 32);
|
|
||||||
GroupName = Encoding.ASCII.GetString(bytes, 0x129, 32);
|
|
||||||
_namePrefix = Encoding.ASCII.GetString(bytes, 347, 157);
|
|
||||||
|
|
||||||
return base.UpdateHeaderFromBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsPathSeparator(char ch)
|
|
||||||
=> ch == '\\' || ch == '/' || ch == '|'; // All the path separators I ever met.
|
|
||||||
|
|
||||||
public override byte[] GetHeaderValue()
|
|
||||||
{
|
|
||||||
byte[] header = base.GetHeaderValue();
|
|
||||||
|
|
||||||
Encoding.ASCII.GetBytes(Magic).CopyTo(header, 0x101); // Mark header as ustar
|
|
||||||
Encoding.ASCII.GetBytes(Version).CopyTo(header, 0x106);
|
|
||||||
Encoding.ASCII.GetBytes(UserName).CopyTo(header, 0x109);
|
|
||||||
Encoding.ASCII.GetBytes(GroupName).CopyTo(header, 0x129);
|
|
||||||
Encoding.ASCII.GetBytes(_namePrefix).CopyTo(header, 347);
|
|
||||||
|
|
||||||
if (SizeInBytes >= 0x1FFFFFFFF)
|
|
||||||
{
|
|
||||||
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(SizeInBytes));
|
|
||||||
SetMarker(AlignTo12(bytes)).CopyTo(header, 124);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecalculateChecksum(header);
|
|
||||||
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(header, 148);
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] SetMarker(byte[] bytes)
|
|
||||||
{
|
|
||||||
bytes[0] |= 0x80;
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] AlignTo12(byte[] bytes)
|
|
||||||
{
|
|
||||||
byte[] retVal = new byte[12];
|
|
||||||
bytes.CopyTo(retVal, 12 - bytes.Length);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Upcoming - 0000-00-00
|
## Upcoming - 0000-00-00
|
||||||
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Solution restructured to use multiple test projects
|
||||||
- Optimized for C# 12
|
- Optimized for C# 12
|
||||||
- `IPNetwork` is used from (as `Microsoft.AspNetCore.HttpOverrides` is tagged as "out of support"):
|
- `IPNetwork` is used from (as `Microsoft.AspNetCore.HttpOverrides` is tagged as "out of support"):
|
||||||
- .NET Standard 2.0 and .NET 6.0: `Microsoft.AspNetCore.HttpOverrides.IPNetwork`
|
- .NET Standard 2.0 and .NET 6.0: `Microsoft.AspNetCore.HttpOverrides.IPNetwork`
|
||||||
|
|||||||
53
Common.sln
53
Common.sln
@@ -3,23 +3,31 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.0.31903.59
|
VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common", "AMWD.Common\AMWD.Common.csproj", "{F512C474-B670-4E47-911E-7C0674AA8E7E}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
src\Directory.Build.props = src\Directory.Build.props
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.AspNetCore", "AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj", "{725F40C9-8172-487F-B3D0-D7E38B4DB197}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
test\Directory.Build.props = test\Directory.Build.props
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.EntityFrameworkCore", "AMWD.Common.EntityFrameworkCore\AMWD.Common.EntityFrameworkCore.csproj", "{7091CECF-C981-4FB9-9CC6-91C4E65A6356}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common", "src\AMWD.Common\AMWD.Common.csproj", "{F512C474-B670-4E47-911E-7C0674AA8E7E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.AspNetCore", "src\AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj", "{725F40C9-8172-487F-B3D0-D7E38B4DB197}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.EntityFrameworkCore", "src\AMWD.Common.EntityFrameworkCore\AMWD.Common.EntityFrameworkCore.csproj", "{7091CECF-C981-4FB9-9CC6-91C4E65A6356}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.MessagePack", "src\AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Test", "src\AMWD.Common.Test\AMWD.Common.Test.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Tests", "test\AMWD.Common.Tests\AMWD.Common.Tests.csproj", "{9469D87B-126E-4338-92E3-701F762CB54D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5}"
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Test", "AMWD.Common.Test\AMWD.Common.Test.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{9469D87B-126E-4338-92E3-701F762CB54D}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
CHANGELOG.md = CHANGELOG.md
|
CHANGELOG.md = CHANGELOG.md
|
||||||
icon.png = icon.png
|
icon.png = icon.png
|
||||||
@@ -27,13 +35,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{86DE1B7C-3
|
|||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{7196DA2B-D858-4B25-BC23-865175CFCDEC}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7196DA2B-D858-4B25-BC23-865175CFCDEC}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.gitlab-ci.yml = .gitlab-ci.yml
|
.gitlab-ci.yml = .gitlab-ci.yml
|
||||||
directory.build.props = directory.build.props
|
directory.build.props = directory.build.props
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16-7DEF-4E39-B590-E804DEF7C607}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{93EC8B16-7DEF-4E39-B590-E804DEF7C607}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
.gitignore = .gitignore
|
.gitignore = .gitignore
|
||||||
@@ -41,7 +49,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16
|
|||||||
nuget.config = nuget.config
|
nuget.config = nuget.config
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.MessagePack", "AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.AspNetCore.Tests", "test\AMWD.Common.AspNetCore.Tests\AMWD.Common.AspNetCore.Tests.csproj", "{2CDA58C2-DE85-478D-9474-A937A20BECD1}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -61,6 +69,10 @@ Global
|
|||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -69,10 +81,10 @@ Global
|
|||||||
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -81,12 +93,13 @@ Global
|
|||||||
{F512C474-B670-4E47-911E-7C0674AA8E7E} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{F512C474-B670-4E47-911E-7C0674AA8E7E} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
|
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{9469D87B-126E-4338-92E3-701F762CB54D} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
|
{9469D87B-126E-4338-92E3-701F762CB54D} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
|
||||||
{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
||||||
{7196DA2B-D858-4B25-BC23-865175CFCDEC} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
{7196DA2B-D858-4B25-BC23-865175CFCDEC} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
||||||
{93EC8B16-7DEF-4E39-B590-E804DEF7C607} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
{93EC8B16-7DEF-4E39-B590-E804DEF7C607} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
|
||||||
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{2CDA58C2-DE85-478D-9474-A937A20BECD1} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}
|
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}
|
||||||
|
|||||||
@@ -1,41 +1,11 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NrtRevisionFormat>{semvertag:main}{!:-dev}</NrtRevisionFormat>
|
<LangVersion>12.0</LangVersion>
|
||||||
|
|
||||||
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
|
||||||
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
|
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
||||||
|
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
|
||||||
<EmbedUntrackedSources>false</EmbedUntrackedSources>
|
|
||||||
|
|
||||||
<PackageProjectUrl>https://wiki.am-wd.de/libs/common</PackageProjectUrl>
|
|
||||||
<Description>Library with classes and extensions used frequently on AM.WD projects.</Description>
|
|
||||||
|
|
||||||
<RepositoryType>git</RepositoryType>
|
|
||||||
<RepositoryUrl>https://git.am-wd.de/AM.WD/common.git</RepositoryUrl>
|
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
|
||||||
|
|
||||||
<Company>AM.WD</Company>
|
<Company>AM.WD</Company>
|
||||||
<Authors>Andreas Müller</Authors>
|
<Authors>Andreas Müller</Authors>
|
||||||
<Copyright>© {copyright:2020-} AM.WD</Copyright>
|
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(CI)' == 'true'">
|
|
||||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(CI)' == 'true'">
|
|
||||||
<SourceLinkGitLabHost Include="$(CI_SERVER_HOST)" Version="$(CI_SERVER_VERSION)" />
|
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitLab" Version="8.0.0" PrivateAssets="all" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AMWD.NetRevisionTask" Version="1.2.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ To save time, they are all packed to NuGet packages.
|
|||||||
| AMWD.Common.EntityFrameworkCore |  |
|
| AMWD.Common.EntityFrameworkCore |  |
|
||||||
| AMWD.Common.MessagePack |  |
|
| AMWD.Common.MessagePack |  |
|
||||||
| AMWD.Common.Test |  |
|
| AMWD.Common.Test |  |
|
||||||
| CI / CD |   |
|
| CI / CD |   |
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace UnitTests.Common.Packing.Tar
|
|
||||||
{
|
|
||||||
internal class TarReaderTests
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<CollectCoverage>true</CollectCoverage>
|
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="DNS" Version="7.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
|
||||||
<PackageReference Include="Moq" Version="4.20.70" />
|
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
|
||||||
<PackageReference Include="ReflectionMagic" Version="5.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj" />
|
|
||||||
<ProjectReference Include="..\AMWD.Common\AMWD.Common.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -2,25 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
|
|
||||||
<NrtTagMatch>asp/v[0-9]*</NrtTagMatch>
|
<AssemblyName>amwd-common-aspnetcore</AssemblyName>
|
||||||
<AssemblyName>AMWD.Common.AspNetCore</AssemblyName>
|
|
||||||
<RootNamespace>AMWD.Common.AspNetCore</RootNamespace>
|
<RootNamespace>AMWD.Common.AspNetCore</RootNamespace>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<NrtTagMatch>asp/v[0-9]*</NrtTagMatch>
|
||||||
<PackageId>AMWD.Common.AspNetCore</PackageId>
|
<PackageId>AMWD.Common.AspNetCore</PackageId>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
|
||||||
|
|
||||||
<Product>AM.WD Common Library for ASP.NET Core</Product>
|
<Product>AM.WD Common Library for ASP.NET Core</Product>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
|
||||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
@@ -2,25 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
|
|
||||||
<NrtTagMatch>efc/v[0-9]*</NrtTagMatch>
|
<AssemblyName>amwd-common-efcore</AssemblyName>
|
||||||
<AssemblyName>AMWD.Common.EntityFrameworkCore</AssemblyName>
|
|
||||||
<RootNamespace>AMWD.Common.EntityFrameworkCore</RootNamespace>
|
<RootNamespace>AMWD.Common.EntityFrameworkCore</RootNamespace>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<NrtTagMatch>efc/v[0-9]*</NrtTagMatch>
|
||||||
<PackageId>AMWD.Common.EntityFrameworkCore</PackageId>
|
<PackageId>AMWD.Common.EntityFrameworkCore</PackageId>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
|
||||||
|
|
||||||
<Product>AM.WD Common Library for EntityFramework Core</Product>
|
<Product>AM.WD Common Library for EntityFramework Core</Product>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
|
||||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
|
||||||
@@ -2,27 +2,17 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
|
|
||||||
<NrtTagMatch>msgpack/v[0-9]*</NrtTagMatch>
|
<AssemblyName>amwd-common-msgpack</AssemblyName>
|
||||||
<AssemblyName>AMWD.Common.MessagePack</AssemblyName>
|
|
||||||
<RootNamespace>AMWD.Common.MessagePack</RootNamespace>
|
<RootNamespace>AMWD.Common.MessagePack</RootNamespace>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<NrtTagMatch>msgpack/v[0-9]*</NrtTagMatch>
|
||||||
<PackageId>AMWD.Common.MessagePack</PackageId>
|
<PackageId>AMWD.Common.MessagePack</PackageId>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
|
||||||
|
|
||||||
<Product>AM.WD Common Library for MessagePack</Product>
|
<Product>AM.WD Common Library for MessagePack</Product>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
<PackageReference Include="MessagePack" Version="2.5.168" />
|
||||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="MessagePack" Version="2.5.140" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
|
||||||
@@ -2,28 +2,18 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
|
|
||||||
<NrtTagMatch>test/v[0-9]*</NrtTagMatch>
|
<AssemblyName>amwd-common-test</AssemblyName>
|
||||||
<AssemblyName>AMWD.Common.Test</AssemblyName>
|
|
||||||
<RootNamespace>AMWD.Common.Test</RootNamespace>
|
<RootNamespace>AMWD.Common.Test</RootNamespace>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<NrtTagMatch>test/v[0-9]*</NrtTagMatch>
|
||||||
<PackageId>AMWD.Common.Test</PackageId>
|
<PackageId>AMWD.Common.Test</PackageId>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
|
||||||
|
|
||||||
<Product>AM.WD Common Library for Unit-Testing</Product>
|
<Product>AM.WD Common Library for Unit-Testing</Product>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
|
||||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Moq" Version="4.20.70" />
|
<PackageReference Include="Moq" Version="4.20.70" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -2,24 +2,14 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
|
|
||||||
<AssemblyName>AMWD.Common</AssemblyName>
|
<AssemblyName>amwd-common</AssemblyName>
|
||||||
<RootNamespace>AMWD.Common</RootNamespace>
|
<RootNamespace>AMWD.Common</RootNamespace>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<PackageId>AMWD.Common</PackageId>
|
<PackageId>AMWD.Common</PackageId>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
|
||||||
|
|
||||||
<Product>AM.WD Common Library</Product>
|
<Product>AM.WD Common Library</Product>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="../icon.png" Pack="true" PackagePath="/" />
|
|
||||||
<None Include="../README.md" Pack="true" PackagePath="/" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
|
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
|
||||||
@@ -41,7 +41,7 @@ namespace System
|
|||||||
if (InvalidHexCharRegex().IsMatch(str))
|
if (InvalidHexCharRegex().IsMatch(str))
|
||||||
yield break;
|
yield break;
|
||||||
#else
|
#else
|
||||||
if (Regex.IsMatch(str, "[^0-9a-fA-F]"))
|
if (Regex.IsMatch(str, "[^0-9a-fA-F]", RegexOptions.Compiled))
|
||||||
yield break;
|
yield break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -180,18 +180,30 @@ namespace System
|
|||||||
/// <returns><c>true</c> when the email address is valid, other wise <c>false</c>.</returns>
|
/// <returns><c>true</c> when the email address is valid, other wise <c>false</c>.</returns>
|
||||||
public static bool IsValidEmailAddress(this string emailAddress, IEnumerable<IPEndPoint> nameservers)
|
public static bool IsValidEmailAddress(this string emailAddress, IEnumerable<IPEndPoint> nameservers)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(emailAddress))
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mailAddress = new MailAddress(emailAddress);
|
var mailAddress = new MailAddress(emailAddress);
|
||||||
bool isValid = mailAddress.Address == emailAddress;
|
if (mailAddress.Address != emailAddress)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (isValid && nameservers?.Any() == true)
|
#if NET8_0_OR_GREATER
|
||||||
|
if (!ValidEmailRegex().IsMatch(emailAddress))
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
string emailRegex = @"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$";
|
||||||
|
if (!Regex.IsMatch(emailAddress, emailRegex, RegexOptions.Compiled))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (nameservers?.Any() == true)
|
||||||
{
|
{
|
||||||
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
|
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
|
||||||
var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS");
|
var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS");
|
||||||
var resolveMethodInfo = dnsClientType.GetMethod("Resolve", [typeof(string), recordTypeType, typeof(CancellationToken)]);
|
var resolveMethodInfo = dnsClientType.GetMethod("Resolve", [typeof(string), recordTypeType, typeof(CancellationToken)]);
|
||||||
|
|
||||||
bool exists = false;
|
|
||||||
foreach (var nameserver in nameservers)
|
foreach (var nameserver in nameservers)
|
||||||
{
|
{
|
||||||
object dnsClient = Activator.CreateInstance(dnsClientType, [nameserver]);
|
object dnsClient = Activator.CreateInstance(dnsClientType, [nameserver]);
|
||||||
@@ -210,21 +222,13 @@ namespace System
|
|||||||
foreach (object item in (list as IEnumerable))
|
foreach (object item in (list as IEnumerable))
|
||||||
{
|
{
|
||||||
int type = (int)item.GetType().GetProperty("Type").GetValue(item);
|
int type = (int)item.GetType().GetProperty("Type").GetValue(item);
|
||||||
if (type == 15)
|
if (type == 15) // MX found
|
||||||
{
|
return true;
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid &= exists;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return isValid;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -244,8 +248,11 @@ namespace System
|
|||||||
=> sb.Append(value).Append(newLine);
|
=> sb.Append(value).Append(newLine);
|
||||||
|
|
||||||
#if NET8_0_OR_GREATER
|
#if NET8_0_OR_GREATER
|
||||||
[GeneratedRegex("[^0-9a-fA-F]")]
|
[GeneratedRegex("[^0-9a-fA-F]", RegexOptions.Compiled)]
|
||||||
private static partial Regex InvalidHexCharRegex();
|
private static partial Regex InvalidHexCharRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$", RegexOptions.Compiled)]
|
||||||
|
private static partial Regex ValidEmailRegex();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
4
src/AMWD.Common/InternalsVisibleTo.cs
Normal file
4
src/AMWD.Common/InternalsVisibleTo.cs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("AMWD.Common.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||||
@@ -14,7 +14,7 @@ namespace AMWD.Common.Packing.Ar
|
|||||||
// Source: http://en.wikipedia.org/wiki/Ar_%28Unix%29
|
// Source: http://en.wikipedia.org/wiki/Ar_%28Unix%29
|
||||||
|
|
||||||
private readonly Stream _inStream;
|
private readonly Stream _inStream;
|
||||||
private readonly List<ArFileInfoExtended> _files = new();
|
private readonly List<ArFileInfoExtended> _files = [];
|
||||||
private readonly long _streamStartPosition;
|
private readonly long _streamStartPosition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,7 +64,7 @@ namespace AMWD.Common.Packing.Ar
|
|||||||
// Align to even offsets, pad with LF bytes
|
// Align to even offsets, pad with LF bytes
|
||||||
if ((_outStream.Position % 2) != 0)
|
if ((_outStream.Position % 2) != 0)
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[] { 0x0A };
|
byte[] bytes = [0x0A];
|
||||||
_outStream.Write(bytes, 0, 1);
|
_outStream.Write(bytes, 0, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,12 +124,12 @@ namespace AMWD.Common.Packing.Ar
|
|||||||
|
|
||||||
// File size in bytes
|
// File size in bytes
|
||||||
if (fileSize < 0 || 10000000000 <= fileSize)
|
if (fileSize < 0 || 10000000000 <= fileSize)
|
||||||
throw new ArgumentOutOfRangeException("Invalid file size."); // above 9.32 GB
|
throw new ArgumentOutOfRangeException(nameof(fileSize), "Invalid file size."); // above 9.32 GB
|
||||||
|
|
||||||
WriteAsciiString(fileSize.ToString().PadRight(10, ' '));
|
WriteAsciiString(fileSize.ToString().PadRight(10, ' '));
|
||||||
|
|
||||||
// File magic
|
// File magic
|
||||||
byte[] bytes = new byte[] { 0x60, 0x0A };
|
byte[] bytes = [0x60, 0x0A];
|
||||||
_outStream.Write(bytes, 0, 2);
|
_outStream.Write(bytes, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user