edge-install/DebUnpack.cs
Robert Morrison 60e06b2144
Another bad commit
Lots of changes here...

- Removed code using RecursivExtractor due to bad usage of /tmp
  Note: the code that used RecursiveExtractor may not have been in the
  previous commit
- Created _functioning_ implementation of DebUnpacker
- Restructured Project layout.

TODO:
- Possibly rewrite how the DebUnpacker works to have the file contents
  part of an entry.
- Further restructuring and refactoring to make the code a little
  neater.
- Add code for the final unpack steps Un-XZ -> untar -> write to disk
- Add code to install
- Add code for modified post-install pre-remove etc..
  This is kinda needed to ensure proper system integration of new
  packages.
2023-06-08 04:36:31 +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]);
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");
}
}
}