Preliminary support for reading tar files

This commit is contained in:
Frederik Carlier
2017-05-14 22:20:18 +02:00
parent 6f83c1130a
commit 1f2b903aef
6 changed files with 338 additions and 2 deletions

View File

@@ -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);
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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'
}
}