1
0

Updated to C# 12

This commit is contained in:
2024-01-14 13:10:33 +01:00
parent 9cd1344266
commit 27cd54fb30
51 changed files with 637 additions and 379 deletions

View File

@@ -7,28 +7,24 @@ 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
/// Extract contents of a tar file represented by a stream for the TarReader constructor.
/// </summary>
/// <remarks>
/// https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarReader.cs
/// 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>
public class TarReader
/// <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;
private readonly Stream _inStream;
private readonly UsTarHeader _header = new();
private readonly Stream _inStream = tarredData;
private long _remainingBytesInFile;
/// <summary>
/// Constructs TarReader object to read data from `tarredData` stream
/// Gets the file info (the header).
/// </summary>
/// <param name="tarredData">A stream to read tar archive from</param>
public TarReader(Stream tarredData)
{
_inStream = tarredData;
_header = new UsTarHeader();
}
public ITarHeader FileInfo => _header;
/// <summary>
@@ -66,7 +62,7 @@ namespace AMWD.Common.Packing.Tar
}
/// <summary>
/// Read data from a current file to a Stream.
/// Read data from the current archive to a Stream.
/// </summary>
/// <param name="dataDestanation">A stream to read data to</param>
/// <seealso cref="MoveNext"/>
@@ -84,6 +80,11 @@ namespace AMWD.Common.Packing.Tar
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)

View File

@@ -5,13 +5,32 @@ using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar
{
// https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs
/// <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()
@@ -29,6 +48,17 @@ namespace AMWD.Common.Packing.Tar
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(
@@ -41,6 +71,16 @@ namespace AMWD.Common.Packing.Tar
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);
@@ -52,6 +92,16 @@ namespace AMWD.Common.Packing.Tar
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)
{

View File

@@ -10,11 +10,14 @@ namespace AMWD.Common.Packing.Tar.Utils
/// Implements a legacy TAR writer.
/// </summary>
/// <remarks>
/// Copied from <a href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/LegacyTarWriter.cs" />
/// 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>
public class LegacyTarWriter : IDisposable
/// <param name="outStream">stream to write archive to</param>
public class LegacyTarWriter(Stream outStream) : IDisposable
{
private readonly Stream _outStream;
private readonly Stream _outStream = outStream;
private bool _isClosed;
/// <summary>
@@ -22,15 +25,6 @@ namespace AMWD.Common.Packing.Tar.Utils
/// </summary>
protected byte[] buffer = new byte[1024];
/// <summary>
/// Writes tar (see GNU tar) archive to a stream
/// </summary>
/// <param name="outStream">stream to write archive to</param>
public LegacyTarWriter(Stream outStream)
{
_outStream = outStream;
}
/// <summary>
/// Gets or sets a value indicating whether to read on zero.
/// </summary>
@@ -48,20 +42,25 @@ namespace AMWD.Common.Packing.Tar.Utils
/// <inheritdoc/>
public void Dispose()
{
Close();
}
=> 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("path");
throw new ArgumentNullException(nameof(path), "The path is not set.");
if (path[path.Length - 1] != '/')
{
path += '/';
}
DateTime lastWriteTime;
if (Directory.Exists(path))
{
@@ -89,40 +88,50 @@ namespace AMWD.Common.Packing.Tar.Utils
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("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("fileName");
using (FileStream file = File.OpenRead(fileName))
{
Write(file, file.Length, fileName, 61, 61, 511, File.GetLastWriteTime(file.Name));
}
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);
@@ -130,22 +139,48 @@ namespace AMWD.Common.Packing.Tar.Utils
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);
}
=> 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)
{
IArchiveDataWriter writer = new DataWriter(OutStream, dataSizeInBytes);
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)
@@ -189,21 +224,26 @@ namespace AMWD.Common.Packing.Tar.Utils
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);
}
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("LegacyTarWriter unable to read from provided stream");
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0)
{
if (ReadOnZero)
@@ -218,7 +258,8 @@ namespace AMWD.Common.Packing.Tar.Utils
{
int bytesRead = data.Read(buffer, 0, (int)count);
if (bytesRead < 0)
throw new IOException("LegacyTarWriter unable to read from provided stream");
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0)
{
while (count > 0)
@@ -232,6 +273,16 @@ namespace AMWD.Common.Packing.Tar.Utils
}
}
/// <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
@@ -247,6 +298,9 @@ namespace AMWD.Common.Packing.Tar.Utils
OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize);
}
/// <summary>
/// Aligns the entry to 512 bytes.
/// </summary>
public void AlignTo512(long size, bool acceptZero)
{
size %= 512;
@@ -258,6 +312,9 @@ namespace AMWD.Common.Packing.Tar.Utils
}
}
/// <summary>
/// Closes the writer and aligns to 512 bytes.
/// </summary>
public virtual void Close()
{
if (_isClosed)

View File

@@ -2,10 +2,51 @@
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// Represents errors that occur during tar archive execution.
/// </summary>
public class TarException : Exception
{
public TarException(string message) : base(message)
{
}
/// <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
}
}