File: src\ZipDataEntry.cs
Web Access
Project: src\src\Microsoft.DotNet.SignTool\Microsoft.DotNet.SignTool.csproj (Microsoft.DotNet.SignTool)
// 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.Immutable;
using System.IO;
using System.IO.Compression;
 
namespace Microsoft.DotNet.SignTool
{
    internal sealed class ZipDataEntry : IDisposable
    {
        string _relativePath;
        Stream _stream;
        ImmutableArray<byte> _contentHash;
 
        public ZipDataEntry(string relativePath, Stream contentStream) : this(relativePath, contentStream, contentStream?.Length ?? 0)
        {
        }
 
        public ZipDataEntry(string relativePath, Stream contentStream, long contentSize)
        {
            _relativePath = relativePath;
 
            // this might be just a pointer to a folder. We skip those.
            if (contentStream == null)
                return;
 
            if (contentStream.CanSeek)
            {
                _stream = contentStream;
            }
            else
            {
                // if we can't seek we need to copy the stream into a (seekable) MemoryStream so we can compute the content hash
                _stream = new MemoryStream((int)contentSize);
                contentStream.CopyTo(_stream);
            }
 
            // compute content hash and reset position back
            _contentHash = ContentUtil.GetContentHash(_stream);
            _stream.Position = 0;
        }
 
        public ZipDataEntry(ZipArchiveEntry entry)
        {
            _relativePath = entry.FullName; // lgtm [cs/zipslip] Archive from trusted source
 
            // this might be just a pointer to a folder. We skip those.
            if (_relativePath.EndsWith("/") && entry.Name == "")
                return;
 
            // the stream returned by entry.Open() isn't seekable,
            // we can avoid creating a MemoryStream copy by just opening a separate instance for computing the content hash
            using var contentHashStream = entry.Open();
            _contentHash = ContentUtil.GetContentHash(contentHashStream);
 
            // this will already be at position 0
            _stream = entry.Open();
        }
 
        public string RelativePath => _relativePath;
 
        public ImmutableArray<byte> ContentHash => _contentHash;
 
        public void WriteToFile(string path)
        {
            using var fs = File.Create(path);
            _stream.CopyTo(fs);
        }
 
        public void Dispose()
        {
            _stream.Dispose();
            _stream = null;
        }
    }
}