File: src\CpioReader.cs
Web Access
Project: src\src\Microsoft.DotNet.Build.Tasks.Installers\Microsoft.DotNet.Build.Tasks.Installers.csproj (Microsoft.DotNet.Build.Tasks.Installers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
 
#nullable enable
 
namespace Microsoft.DotNet.Build.Tasks.Installers
{
    internal sealed class CpioReader(Stream stream, bool leaveOpen) : IDisposable
    {
        public CpioEntry? GetNextEntry()
        {
            if (stream.Position == stream.Length)
            {
                return null;
            }
 
            byte[] magic = new byte[6];
            stream.ReadExactly(magic, 0, 6);
            if (!magic.AsSpan().SequenceEqual("070701"u8))
            {
                throw new InvalidDataException("Invalid header magic");
            }
 
            byte[] byteBuffer = new byte[8];
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong inode = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            uint mode = uint.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong ownerID = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong groupID = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ushort numberOfLinks = ushort.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong timestamp = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong size = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong devMajor = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong devMinor = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong rdevMajor = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            ulong rdevMinor = ulong.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            stream.ReadExactly(byteBuffer, 0, 8);
            int namesize = int.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            // Ignore checksum
            stream.ReadExactly(byteBuffer, 0, 8);
            _ = int.Parse(Encoding.ASCII.GetString(byteBuffer), NumberStyles.HexNumber);
 
            byte[] nameBuffer = new byte[namesize];
            stream.ReadExactly(nameBuffer, 0, namesize);
            string name = Encoding.ASCII.GetString(nameBuffer, 0, namesize - 1);
 
            stream.AlignReadTo(4);
 
            byte[] data = new byte[size];
            stream.ReadExactly(data, 0, (int)size);            
            MemoryStream dataStream = new(data);
 
            stream.AlignReadTo(4);
 
            if (name == "TRAILER!!!")
            {
                // We've reached the end of the archive.
                return null;
            }
 
            return new CpioEntry(
                inode,
                name,
                timestamp,
                ownerID,
                groupID,
                mode,
                numberOfLinks,
                devMajor,
                devMinor,
                rdevMajor,
                rdevMinor,
                dataStream);
        }
 
        public void Dispose()
        {
            if (!leaveOpen)
            {
                stream.Dispose();
            }
        }
    }
}