private void ReadFiles(ByteImageReader rdr, ArArchive archive) { for (; ;) { var header = ArFileHeader.Load(rdr); if (header == null) { return; } ReadFile(header, rdr, archive); } }
private void ReadFile(ArFileHeader fileHeader, ByteImageReader rdr, ArArchive archive) { if (!int.TryParse(fileHeader.FileSize, out int dataSize)) { throw new BadImageFormatException("Invalid archive file header."); } if (dataSize + rdr.Offset > rdr.Bytes.Length) { throw new BadImageFormatException("The archive file is corrupt."); } string name = fileHeader.Name; if (name.StartsWith("// ")) { throw new NotImplementedException("Extended file names not implemented yet."); } else if (name.StartsWith("/ ") || name.StartsWith("__.SYMDEF")) { // System V symbol lookup table. var symbolData = rdr.ReadBytes(dataSize); ReadSymbolTable(symbolData); return; } else if (name.StartsWith("#1/ ")) { // File name length follows #1/ as decimal digits. // This variant is used by Mac and some versions of BSD. var fileNameLength = Convert.ToInt32(name + 3); long fileDataOffset = rdr.Offset + fileNameLength; name = Encoding.ASCII.GetString(rdr.ReadBytes(fileNameLength)); if (dataSize > fileNameLength) { // The length of the name is included in the dataSize. dataSize -= fileNameLength; } rdr.Offset = fileDataOffset; } else { // Ordinary short name char[] charsToTrim = { '/', ' ' }; name = name.TrimEnd(charsToTrim); } archive.AddFile(name, (a, p, name) => new ArFile(a, p, name, rdr, rdr.Offset, dataSize)); rdr.Offset += dataSize; AlignReader(rdr); }
private static async Task ReadDebAsync( string debPath, string outputDir, Func <string, string> getEntryPath, bool shouldExtract) { // Debian packages (.deb) are "ar" archives with three files. // // 8 bytes for signature (should be "!<arch>\n") // // Each section has the following header: // - 16 bytes for file identifier (ASCII) // - 12 bytes for file modification timestamp (decimal) // - 6 bytes for owner ID (decimal) // - 6 bytes for group ID (decimal) // - 8 bytes for file mode (octal) // - 10 bytes for file size (decimal) // - 2 bytes for ending characters ("`\n") // // The three sections are: // - Package Section // - Control Section // - Data Section // // We only care about the data section and we expect it to be a .tar.xz. const int signatureLength = 8; const int headerLength = 16 + 12 + 6 + 6 + 8 + 10 + 2; var buffer = new byte[headerLength]; using (var fileStream = new FileStream(debPath, FileMode.Open)) { var read = fileStream.Read(buffer, 0, signatureLength); if (read != signatureLength || Encoding.ASCII.GetString(buffer, 0, read) != "!<arch>\n") { throw new TorSharpException("The Debian package did not have the expected file signature."); } read = fileStream.Read(buffer, 0, headerLength); var packageSectionHeader = ArFileHeader.Read(buffer); if (read != headerLength || packageSectionHeader.FileSize != 4) { throw new TorSharpException("The Debian package did not have the expected package section file header."); } fileStream.Position += packageSectionHeader.FileSize; read = fileStream.Read(buffer, 0, headerLength); var controlSectionHeader = ArFileHeader.Read(buffer); fileStream.Position += controlSectionHeader.FileSize; read = fileStream.Read(buffer, 0, headerLength); var dataSectionHeader = ArFileHeader.Read(buffer); var trimmedFileIdentifier = dataSectionHeader.FileIdentifier.TrimEnd(); if (!trimmedFileIdentifier.EndsWith(".tar.xz")) { throw new TorSharpException("The Debian package's data section is expected to be a .tar.xz file."); } if (fileStream.Position + dataSectionHeader.FileSize != fileStream.Length) { throw new TorSharpException("The Debian package's data section is expected to reach the end of the .dev file."); } if (!shouldExtract) { await ReadTarXzAsync( fileStream, outputDir, getEntryPath, shouldExtract : false).ConfigureAwait(false); } else { await ReadTarXzAsync( fileStream, outputDir, getEntryPath, shouldExtract : true).ConfigureAwait(false); } } }