mirror of
https://github.com/quamotion/dotnet-packaging.git
synced 2026-05-19 13:44:11 +00:00
Preliminary support for reading tar files
This commit is contained in:
@@ -27,7 +27,7 @@ namespace Packaging.Targets.IO
|
||||
/// Initializes a new instance of the <see cref="ArchiveFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">
|
||||
/// A <see cref="Stream"/> which represents the CPIO data.
|
||||
/// A <see cref="Stream"/> which represents the archive data.
|
||||
/// </param>
|
||||
/// <param name="leaveOpen">
|
||||
/// <see langword="true"/> to leave the underlying <paramref name="stream"/> open when this <see cref="ArchiveFile"/>
|
||||
@@ -138,7 +138,7 @@ namespace Packaging.Targets.IO
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] buffer = new byte[PaddingSize(alignmentBase, (int)this.EntryStream.Length)];
|
||||
byte[] buffer = new byte[PaddingSize(alignmentBase, (int)this.Stream.Position)];
|
||||
this.Stream.Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
34
Packaging.Targets/IO/GZipDecompressor.cs
Normal file
34
Packaging.Targets/IO/GZipDecompressor.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides read-only access do a decompressed <see cref="GZipStream"/>, and keeps track of the current position.
|
||||
/// </summary>
|
||||
internal class GZipDecompressor : GZipStream
|
||||
{
|
||||
private long position = 0;
|
||||
|
||||
public GZipDecompressor(Stream stream, bool leaveOpen)
|
||||
: base(stream, CompressionMode.Decompress, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Position
|
||||
{
|
||||
get { return this.position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Read(byte[] array, int offset, int count)
|
||||
{
|
||||
var read = base.Read(array, offset, count);
|
||||
this.position += read;
|
||||
return read;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Packaging.Targets/IO/TarFile.cs
Normal file
61
Packaging.Targets/IO/TarFile.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <c>Tar</c> archive.
|
||||
/// </summary>
|
||||
public class TarFile : ArchiveFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TarFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">
|
||||
/// A <see cref="Stream"/> which represents the tar file.
|
||||
/// </param>
|
||||
/// <param name="leaveOpen">
|
||||
/// <see langword="true"/> to leave the underlying <paramref name="stream"/> open when this <see cref="TarFile"/>
|
||||
/// is disposed of; otherwise, <see langword="false"/>.
|
||||
/// </param>
|
||||
public TarFile(Stream stream, bool leaveOpen)
|
||||
: base(stream, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
private TarHeader entryHeader;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Read()
|
||||
{
|
||||
if (this.EntryStream != null)
|
||||
{
|
||||
this.EntryStream.Dispose();
|
||||
}
|
||||
|
||||
this.Align(512);
|
||||
|
||||
this.entryHeader = this.Stream.ReadStruct<TarHeader>();
|
||||
this.FileHeader = this.entryHeader;
|
||||
this.FileName = this.entryHeader.FileName;
|
||||
|
||||
// There are two empty blocks at the end of the file.
|
||||
if (this.entryHeader.Magic == string.Empty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.entryHeader.Magic != "ustar")
|
||||
{
|
||||
throw new InvalidDataException("The magic for the file entry is invalid");
|
||||
}
|
||||
|
||||
this.Align(512);
|
||||
|
||||
// TODO: Validate Checksum
|
||||
this.EntryStream = new SubStream(this.Stream, this.Stream.Position, this.entryHeader.FileSize, leaveParentOpen: true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
169
Packaging.Targets/IO/TarHeader.cs
Normal file
169
Packaging.Targets/IO/TarHeader.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the header for an individual entry in a <c>.tar</c> archive.
|
||||
/// </summary>
|
||||
internal struct TarHeader : IArchiveHeader
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 100)]
|
||||
private char[] name;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] mode;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] uid;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] gid;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 12)]
|
||||
private char[] size;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 12)]
|
||||
private char[] mtime;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] chksum;
|
||||
|
||||
private byte typeflag;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 100)]
|
||||
private char[] linkname;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 6)]
|
||||
private char[] magic;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 2)]
|
||||
private char[] version;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 32)]
|
||||
private char[] uname;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 32)]
|
||||
private char[] gname;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] devmajor;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 8)]
|
||||
private char[] devminor;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 155)]
|
||||
private char[] prefix;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the current file.
|
||||
/// </summary>
|
||||
public string FileName
|
||||
{
|
||||
get { return this.GetString(this.name); }
|
||||
set { this.name = value.ToCharArray(); }
|
||||
}
|
||||
|
||||
public LinuxFileMode FileMode
|
||||
{
|
||||
get { return (LinuxFileMode)Convert.ToUInt32(this.GetString(this.mode), 8); }
|
||||
set { this.mode = ((uint)value).ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint UserId
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.uid), 8); }
|
||||
set { this.uid = value.ToString().ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint GroupId
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.gid), 8); }
|
||||
set { this.gid = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint FileSize
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.size), 8); }
|
||||
set { this.size = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public DateTimeOffset LastModified
|
||||
{
|
||||
get { return DateTimeOffset.FromUnixTimeSeconds((long)Convert.ToUInt32(this.GetString(this.mtime))); }
|
||||
set { this.mtime = value.ToUnixTimeSeconds().ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint Checksum
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.chksum), 8); }
|
||||
set { this.chksum = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public TarTypeFlag TypeFlag
|
||||
{
|
||||
get { return (TarTypeFlag)this.typeflag; }
|
||||
set { this.typeflag = (byte)value; }
|
||||
}
|
||||
|
||||
public string LinkName
|
||||
{
|
||||
get { return this.GetString(this.linkname); }
|
||||
set { this.linkname = value.ToCharArray(); }
|
||||
}
|
||||
|
||||
public string Magic
|
||||
{
|
||||
get { return this.GetString(this.magic).Trim(); }
|
||||
set { this.magic = value.PadRight(6).ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint Version
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.version), 8); }
|
||||
set { this.version = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public string UserName
|
||||
{
|
||||
get { return this.GetString(this.uname); }
|
||||
set { this.uname = value.ToCharArray(); }
|
||||
}
|
||||
|
||||
public string GroupName
|
||||
{
|
||||
get { return this.GetString(this.gname); }
|
||||
set { this.gname = value.ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint DevMajor
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.devmajor), 8); }
|
||||
set { this.devmajor = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public uint DevMinor
|
||||
{
|
||||
get { return Convert.ToUInt32(this.GetString(this.devminor), 8); }
|
||||
set { this.devminor = value.ToString("x8").ToCharArray(); }
|
||||
}
|
||||
|
||||
public string Prefix
|
||||
{
|
||||
get { return this.GetString(this.prefix); }
|
||||
set { this.prefix = value.ToCharArray(); }
|
||||
}
|
||||
|
||||
private string GetString(char[] buffer)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
while (count < buffer.Length && buffer[count] != '\0')
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
return new string(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Packaging.Targets/IO/TarTypeFlag.cs
Normal file
21
Packaging.Targets/IO/TarTypeFlag.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Packaging.Targets.IO
|
||||
{
|
||||
internal enum TarTypeFlag : byte
|
||||
{
|
||||
RegType = (byte)'0',
|
||||
ARegType = (byte)'\0',
|
||||
LnkType = (byte)'1',
|
||||
SymType = (byte)'2',
|
||||
ChrType = (byte)'3',
|
||||
BlkType = (byte)'4',
|
||||
DirType = (byte)'5',
|
||||
FifoType = (byte)'6',
|
||||
ConttType = (byte)'7',
|
||||
ExtendedHeader = (byte)'x',
|
||||
GlobalExtendedHeader = (byte)'g'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user