edge-install/DebUnpack.cs
Robert Morrison 79063f97ee
feat: Add installing
It can install now.
Requires pexec to be present

This may be due to an upstream issue with spectre.console affecting how
stdin/stdout are handled.

TODO:
 - Add backup/restore functionality
 - Refactor sections for neatness
 - Improve UX
2023-06-08 11:22:00 +01:00

146 lines
4.5 KiB
C#

using System.Text;
using Serilog;
namespace EdgeInstaller;
sealed class DebFile : IDisposable
{
const int HeaderBytes = 60;
const string FileMagic = "!<arch>\n";
private MemoryStream _fileStream;
public List<DebEntry> fileEntries { get; private set; }
public DebFile(Stream fs)
{
Log.Information("Creating new debFile from stream");
fileEntries = new();
if (fs is null)
{
throw new NullReferenceException();
}
fs.Position = 0;
// read the first 8 bytes and check for the signature
byte[] magicBuffer = new byte[8];
fs.Read(magicBuffer);
Log.Debug("Magic = {magicBuffer}", Encoding.ASCII.GetString(magicBuffer));
if (Encoding.ASCII.GetString(magicBuffer) != FileMagic)
{
throw new ArgumentException(message: "Magic Fail");
}
fs.Position = 0;
_fileStream = new();
fs.CopyTo(_fileStream);
fs.Dispose();
// if we got here then we must have a proper archive.
// We shall now get the files that are inside.
// from OUR copy of the stream we seek to after the magic byte
_fileStream.Position = 8;
while (true) // beware making an infinite loop
{
// Read a header
byte[] header = new byte[60];
var read = _fileStream.Read(header);
// If nothing was read we have hit the end
if (read < 60) // also handle broken headers
{
break;
}
// Otherwise process as a header
var entry = new DebEntry(header, _fileStream.Position);
fileEntries.Add(entry);
// Using the obtained size seek forwards to the next potential header
_fileStream.Seek(entry.sizeBytes, SeekOrigin.Current);
}
}
public bool getFile(DebEntry file, out Stream output)
{
// If we are given a file that isn't in this archive we exit nicely
if (!fileEntries.Contains(file))
{
Log.Error("The given file entry is not valid for this DebFile");
output = Stream.Null;
return false;
}
// Since we have a file that is in the archive
// grab the metadata we need
long offset = file.offset;
long size = file.sizeBytes;
// stream the file to the output stream
_fileStream.Seek(offset, SeekOrigin.Begin);
byte[] fileBuffer = new byte[size];
var count = _fileStream.Read(fileBuffer);
if (count != size)
{
Log.Error("Didn't read the correct amount of data from stream (expected:{size} got:{count})", size, count);
output = Stream.Null;
return false;
}
output = new MemoryStream(fileBuffer); // convert the read buffer to a stream
return true;
}
public void Dispose()
{ // make sure we can clean up our own mess
_fileStream.Dispose();
}
}
sealed class DebEntry
{
// Escape sequnces are used in this string read as "`\n"
const string HeaderMagic = "\x60\n";
public string name { get; private set; }
public long mtime { get; private set; }
public int mode { get; private set; }
public int gid { get; private set; }
public int uid { get; private set; }
public long sizeBytes { get; private set; }
public long offset { get; private set; } // where does the file start
public DebEntry(byte[] buffer, long Position)
{
Log.Debug("Reading header at position {p}", Position - 60); // since we are being passed a header we take 60 to get the header position
if (buffer is null)
{
throw new NullReferenceException();
}
if (buffer.Length < 60)
{
throw new ArgumentException("buffer too short");
}
name = Encoding.ASCII.GetString(buffer[0..16]).Trim(); // Trim any trailing whitespace
mtime = long.Parse(Encoding.ASCII.GetString(buffer[16..28]));
mode = int.Parse(Encoding.ASCII.GetString(buffer[28..34]));
gid = int.Parse(Encoding.ASCII.GetString(buffer[34..40]));
uid = int.Parse(Encoding.ASCII.GetString(buffer[40..48]));
sizeBytes = long.Parse(Encoding.ASCII.GetString(buffer[48..58]));
string magic = Encoding.ASCII.GetString(buffer[58..^0]);
offset = Position;
Log.Debug("Header Magic = {magic}", magic);
Log.Debug("{@this}", this);
if (magic != HeaderMagic)
{
throw new ArgumentException("magic failed");
}
}
}