|
//-----------------------------------------------------------------------------
//------------- *** WARNING ***
//------------- This file is part of a legally monitored development project.
//------------- Do not check in changes to this project. Do not raid bugs on this
//------------- code in the main PS database. Do not contact the owner of this
//------------- code directly. Contact the legal team at ‘ZSLegal’ for assistance.
//------------- *** WARNING ***
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// <copyright file="ZipIOLocalFileBlock.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved.
// </copyright>
//
// Description:
// This is an internal class that enables interactions with Zip archives
// for OPC scenarios
//
// History:
// 11/19/2004: IgorBel: Initial creation.
// 10/21/2005: brucemac: Apply security mitigations
//
//-----------------------------------------------------------------------------
using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;
using System.Globalization;
using System.Windows;
using MS.Internal.IO.Packaging; // For CompressStream
using MS.Internal.WindowsBase;
namespace MS.Internal.IO.Zip
{
internal class ZipIOLocalFileBlock : IZipIOBlock, IDisposable
{
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
// standard IZipIOBlock functionality
public long Offset
{
get
{
CheckDisposed();
return _offset;
}
}
public long Size
{
get
{
CheckDisposed();
checked
{
long size = _localFileHeader.Size + _fileItemStream.Length;
if (_localFileDataDescriptor != null)
{
// we only account for the data descriptor
// if it is there and data wasn't changed yet ,
// because we will discard it as a part of saving
size += _localFileDataDescriptor.Size;
}
return size;
}
}
}
public bool GetDirtyFlag(bool closingFlag)
{
CheckDisposed();
bool deflateStreamDirty = false;
if (_deflateStream != null)
deflateStreamDirty = ((CompressStream) _deflateStream).IsDirty(closingFlag);
// !!! ATTENTION !!!!
// We know for a fact that ZipIoModeEnforcingStream doesn't perform any buffering and is never "dirty".
// In the past we had Dirty flag on the ZipIoModeEnforcing stream that was always false. Enumerating
// those flags had significant perf cost (allocating all the Enumerator classes). We are removing Dirty flag
// from the ZipIoModeEnforcingStream and all the processing code associated with that.
//If at any point we choose to add some buffering to the ZipIoModeEnforcingStream we will have to
// reintroduce Dirty state/flag and properly account for this value in the ZipIoLocalFileBlock.DirtyFlag.
return _dirtyFlag || _fileItemStream.DirtyFlag || deflateStreamDirty;
}
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
public void Move(long shiftSize)
{
CheckDisposed();
Debug.Assert(!_blockManager.Streaming, "Not legal in Streaming mode");
if (shiftSize != 0)
{
checked{_offset +=shiftSize;}
_fileItemStream.Move(shiftSize);
_dirtyFlag = true;
Debug.Assert(_offset >=0);
}
}
/// <summary>
/// Streaming-specific variant of Save()
/// </summary>
/// <param name="closingFlag"></param>
internal void SaveStreaming(bool closingFlag)
{
CheckDisposed();
Debug.Assert(_blockManager.Streaming, "Only legal in Streaming mode");
if (GetDirtyFlag(closingFlag))
{
BinaryWriter writer = _blockManager.BinaryWriter;
// write the local file header if not already done so
if (!_localFileHeaderSaved)
{
// our first access to the ArchiveStream - note our offset
_offset = _blockManager.Stream.Position;
_localFileHeader.Save(writer);
_localFileHeaderSaved = true;
}
FlushExposedStreams();
//this will cause the actual write to disk, and it safe to do so,
// because all we're in streaming mode and there is
// no data in the way
_fileItemStream.SaveStreaming();
// Data Descriptor required for streaming mode
if (closingFlag)
{
// now prior to possibly closing streams we need to preserve uncompressed Size
// otherwise Length function will fail to give it to us later after closing
_localFileDataDescriptor.UncompressedSize = _crcCalculatingStream.Length;
// calculate CRC prior to closing
_localFileDataDescriptor.Crc32 = _crcCalculatingStream.CalculateCrc();
// If we are closing we can do extra things , calculate CRC , close deflate stream
// it is particularly important to close the deflate stream as it might hold some extra bytes
// even after Flush()
// close outstanding streams to signal that we need new pieces if more data comes
CloseExposedStreams();
// in order to get proper compressed size we have to close the deflate stream
if (_deflateStream != null)
{
_deflateStream.Close();
_fileItemStream.SaveStreaming(); // get the extra bytes emitted by the DeflateStream
}
_localFileDataDescriptor.CompressedSize = _fileItemStream.Length;
_localFileDataDescriptor.Save(writer);
_dirtyFlag = false;
}
}
}
/// <summary>
/// Save()
/// </summary>
public void Save()
{
CheckDisposed();
Debug.Assert(!_blockManager.Streaming, "Not legal in Streaming mode");
// Note: This triggers a call to UpdateReferences() which will
// discard any _localFileDataDescriptor.
if (GetDirtyFlag(true)) // if we do not have closingFlag value (we should be using closingFlag=true as a more conservative approach)
{
// We need to notify the _fileItemStream that we about to save our FileHeader;
// otherwise we might be overriding some of the FileItemStream data with the
// FileHeader. Specifically we are concerned about scenario when a previous
// block become large by just a couple of bytes, so that the PreSaveNotification
// issued prior to saving the previous block didn’t trigger caching of our FileItemStream,
// but we still need to make sure that the current FileHeader will not override any data
// in our FileItemStream.
_fileItemStream.PreSaveNotification(_offset, _localFileHeader.Size);
//position the stream
BinaryWriter writer = _blockManager.BinaryWriter;
if (_blockManager.Stream.Position != _offset)
{
// we need to seek
_blockManager.Stream.Seek(_offset, SeekOrigin.Begin);
}
_localFileHeader.Save(writer);
//this will cause the actual write to disk, and it safe to do so,
// because all overlapping data was moved out of the way
// by the calling BlockManager
_fileItemStream.Save();
_dirtyFlag = false;
}
}
// !!! ATTENTION !!!! This function is only supposed to be called by
// Block Manager.Save which has proper protection to ensure no stack overflow will happen
// as a result of Stream.Flush calls which in turn result in BlockManager.Save calls
public void UpdateReferences(bool closingFlag)
{
Invariant.Assert(!_blockManager.Streaming);
long uncompressedSize;
long compressedSize;
CheckDisposed();
if (closingFlag)
{
CloseExposedStreams();
}
else
{
FlushExposedStreams();
}
// At this point we can update Local Headers with the proper CRC Value
// We can also update other Local File Header Values (compressed/uncompressed size)
// we rely on our DirtyFlag property to properly account for all possbile modifications within streams
if (GetDirtyFlag(closingFlag))
{
// Remember the size of the header before update
long headerSizeBeforeUpdate = _localFileHeader.Size;
// now prior to possibly closing streams we need to preserve uncompressed Size
// otherwise Length function will fail to give it to us later after closing
uncompressedSize = _crcCalculatingStream.Length;
// calculate CRC prior to closing
_localFileHeader.Crc32 = _crcCalculatingStream.CalculateCrc();
// If we are closing we can do extra things , calculate CRC , close deflate stream
// it is particularly important to close the deflate stream as it might hold some extra bytes
// even after Flush()
if (closingFlag)
{
// we have got the CRC so we can close the stream
_crcCalculatingStream.Close();
// in order to get proper compressed size we have to close the deflate stream
if (_deflateStream != null)
{
_deflateStream.Close();
}
}
if (_fileItemStream.DataChanged)
{
_localFileHeader.LastModFileDateTime = ZipIOBlockManager.ToMsDosDateTime(DateTime.Now);
}
// get compressed size after possible closing Deflated stream
// as a result of some ineffeciencies in CRC calculation it might result in Seek in compressed stream
// and there fore switching mode and flushing extra compressed bytes
compressedSize = _fileItemStream.Length;
// this will properly (taking into account ZIP64 scenario) update local file header
// Offset is passed in to determine whether ZIP 64 is required for small files that
// happened to be located required 32 bit offset in the archive
_localFileHeader.UpdateZip64Structures(compressedSize, uncompressedSize, Offset);
// Add/remove padding to compensate the header size change
// NOTE: Padding needs to be updated only after updating all the header fields
// that can affect the header size
_localFileHeader.UpdatePadding(_localFileHeader.Size - headerSizeBeforeUpdate);
// We always save File Items in Non-streaming mode unless it wasn't touched
//in which case we leave them alone
_localFileHeader.StreamingCreationFlag = false;
_localFileDataDescriptor = null;
// in some cases UpdateZip64Structures call might result in creation/removal
// of extra field if such thing happened we need to move FileItemStream appropriatel
_fileItemStream.Move(checked(Offset + _localFileHeader.Size - _fileItemStream.Offset));
_dirtyFlag = true;
}
#if FALSE
// we would like to take this oppportunity and validate basic asumption
// that our GetDirtyFlag method is a reliable way to finding changes
// there is no scenario in which change will happened, affecting sizes
// and will not be registered by the GetDirtyFlag
// ???????????????????????
else
{
// we even willing to recalculate CRC just in case for verification purposes
UInt32 calculatedCRC32 = CalculateCrc32();
if (!_localFileHeader.StreamingCreationFlag)
{
Debug.Assert(_localFileHeader.Crc32 == calculatedCRC32);
Debug.Assert(_localFileHeader.CompressedSize == CompressedSize);
Debug.Assert(_localFileHeader.UncompressedSize == UncompressedSize);
}
else
{
Debug.Assert(_localFileDataDescriptor.Crc32 == calculatedCRC32);
Debug.Assert(_localFileDataDescriptor.CompressedSize == CompressedSize);
Debug.Assert(_localFileDataDescriptor.UncompressedSize == UncompressedSize);
}
///////////////////////////////////////////////////////////////////////
// we do not have an initialized value for the compressed size in this case
compressedSize = _fileItemStream.Length;
Debug.Assert(CompressedSize == compressedSize);
Debug.Assert(UncompressedSize == uncompressedSize);
}
#endif
}
public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
{
CheckDisposed();
// local file header and data descryptor are completely cached
// we only need to worry about the actual data
return _fileItemStream.PreSaveNotification(offset, size);
}
/// <summary>
/// Dispose pattern - required implementation for classes that introduce IDisposable
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // not strictly necessary, but if we ever have a subclass with a finalizer, this will be more efficient
}
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
internal static ZipIOLocalFileBlock SeekableLoad (ZipIOBlockManager blockManager,
string fileName)
{
Debug.Assert(!blockManager.Streaming);
Debug.Assert(blockManager.CentralDirectoryBlock.FileExists(fileName));
// Get info from the central directory
ZipIOCentralDirectoryBlock centralDir = blockManager.CentralDirectoryBlock;
ZipIOCentralDirectoryFileHeader centralDirFileHeader = centralDir.GetCentralDirectoryFileHeader(fileName);
long localHeaderOffset = centralDirFileHeader.OffsetOfLocalHeader;
bool folderFlag = centralDirFileHeader.FolderFlag;
bool volumeLabelFlag = centralDirFileHeader.VolumeLabelFlag;
blockManager.Stream.Seek(localHeaderOffset, SeekOrigin.Begin);
ZipIOLocalFileBlock block = new ZipIOLocalFileBlock(blockManager, folderFlag, volumeLabelFlag);
block.ParseRecord(
blockManager.BinaryReader,
fileName,
localHeaderOffset,
centralDir,
centralDirFileHeader);
return block;
}
internal static ZipIOLocalFileBlock CreateNew(ZipIOBlockManager blockManager,
string fileName,
CompressionMethodEnum compressionMethod,
DeflateOptionEnum deflateOption)
{
//this should be ensured by the higher levels
Debug.Assert(Enum.IsDefined(typeof(CompressionMethodEnum), compressionMethod));
Debug.Assert(Enum.IsDefined(typeof(DeflateOptionEnum), deflateOption));
ZipIOLocalFileBlock block = new ZipIOLocalFileBlock(blockManager, false, false);
block._localFileHeader = ZipIOLocalFileHeader.CreateNew
(fileName,
blockManager.Encoding,
compressionMethod,
deflateOption, blockManager.Streaming);
// if in streaming mode - force to Zip64 mode in case the streams get large
if (blockManager.Streaming)
{
block._localFileDataDescriptor = ZipIOLocalFileDataDescriptor.CreateNew();
}
block._offset = 0; // intial value, that is not too important for the brand new File item
block._dirtyFlag = true;
block._fileItemStream = new ZipIOFileItemStream(blockManager,
block,
block._offset + block._localFileHeader.Size,
0);
// create deflate wrapper if necessary
if (compressionMethod == CompressionMethodEnum.Deflated)
{
Debug.Assert(block._fileItemStream.Position == 0, "CompressStream assumes base stream is at position zero");
// Pass bool to indicate that this stream is "new" and must be dirty so that
// the valid empty deflate stream is emitted (2-byte sequence - see CompressStream for details).
block._deflateStream = new CompressStream(block._fileItemStream, 0, true);
block._crcCalculatingStream = new ProgressiveCrcCalculatingStream(blockManager, block._deflateStream);
}
else
{
block._crcCalculatingStream = new ProgressiveCrcCalculatingStream(blockManager, block._fileItemStream);
}
return block;
}
internal Stream GetStream(FileMode mode, FileAccess access)
{
CheckDisposed();
// the main stream held by block Manager must be compatible with the request
CheckFileAccessParameter(_blockManager.Stream, access);
// validate mode and Access
switch(mode)
{
case FileMode.Create:
// Check to make sure that stream isn't read only
if (!_blockManager.Stream.CanWrite)
{
throw new InvalidOperationException(SR.Get(SRID.CanNotWriteInReadOnlyMode));
}
if (_crcCalculatingStream != null && !_blockManager.Streaming)
{
_crcCalculatingStream.SetLength(0);
}
break;
case FileMode.Open:
break;
case FileMode.OpenOrCreate:
break;
case FileMode.CreateNew:
// because we deal with the GetStream call CreateNew is a really strange
// request, as the FileInfo is already there
throw new ArgumentException(SR.Get(SRID.FileModeUnsupported, "CreateNew"));
case FileMode.Append:
throw new ArgumentException(SR.Get(SRID.FileModeUnsupported, "Append"));
case FileMode.Truncate:
throw new ArgumentException(SR.Get(SRID.FileModeUnsupported, "Truncate"));
default:
throw new ArgumentOutOfRangeException("mode");
}
// Streaming mode: always return the same stream (if it exists already)
Stream exposedStream;
if (_blockManager.Streaming && _exposedPublicStreams != null && _exposedPublicStreams.Count > 0)
{
Debug.Assert(_exposedPublicStreams.Count == 1, "Should only be one stream returned in streaming mode");
exposedStream = (Stream)_exposedPublicStreams[0];
}
else
{
Debug.Assert((!_blockManager.Streaming) || (_exposedPublicStreams == null),
"Should be first and only stream returned in streaming mode");
exposedStream = new ZipIOModeEnforcingStream(_crcCalculatingStream, access, _blockManager, this);
RegisterExposedStream(exposedStream);
}
return exposedStream;
}
// NOTE: This method should NOT be called anywhere else except from ZipIOModeEnforcingStream.Dispose(bool)
// This is not designed to be the part of the cyclic process of flushing
internal void DeregisterExposedStream(Stream exposedStream)
{
Debug.Assert(_exposedPublicStreams != null);
_exposedPublicStreams.Remove(exposedStream);
}
/// <summary>
/// Throwes exception if object already Disposed/Closed. This is the only internal
/// (and not private CheckDisposed method). It ismade internal for ZipFileInfo to call
/// </summary>
internal void CheckDisposed()
{
if (_disposedFlag)
{
throw new ObjectDisposedException(null, SR.Get(SRID.ZipFileItemDisposed));
}
}
//------------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
internal UInt16 VersionNeededToExtract
{
get
{
CheckDisposed();
return _localFileHeader.VersionNeededToExtract;
}
}
internal UInt16 GeneralPurposeBitFlag
{
get
{
CheckDisposed();
return _localFileHeader.GeneralPurposeBitFlag;
}
}
internal CompressionMethodEnum CompressionMethod
{
get
{
CheckDisposed();
return _localFileHeader.CompressionMethod;
}
}
internal UInt32 LastModFileDateTime
{
get
{
CheckDisposed();
return _localFileHeader.LastModFileDateTime;
}
}
/// <summary>
/// Return stale CRC value stored in the header.
/// This property doesn't flush streams nor does it recalculates CRC BY DESIGN
/// all updates and revcalculations should be made as a part of the UpdateReferences function
/// which is called by the BlockManager.Save
/// </summary>
internal UInt32 Crc32
{
get
{
CheckDisposed();
if (_localFileHeader.StreamingCreationFlag)
{
Invariant.Assert(_localFileDataDescriptor != null);
return _localFileDataDescriptor.Crc32;
}
else
{
return _localFileHeader.Crc32;
}
}
}
/// <summary>
/// Return stale Compressed Size based on the local file header
/// This property doesn't flush streams, so it is possible that
/// this value will be out of date if Updatereferences isn't
/// called before getting this property
/// </summary>
internal long CompressedSize
{
get
{
CheckDisposed();
if (_localFileHeader.StreamingCreationFlag)
{
Invariant.Assert(_localFileDataDescriptor != null);
return _localFileDataDescriptor.CompressedSize;
}
else
{
return _localFileHeader.CompressedSize;
}
}
}
/// <summary>
/// Return possibly stale Uncompressed Size based on the local file header
/// This property doesn't flush streams, so it is possible that
/// this value will be out of date if Updatereferences isn't
/// called before getting this property
/// </summary>
internal long UncompressedSize
{
get
{
CheckDisposed();
if (_localFileHeader.StreamingCreationFlag)
{
Invariant.Assert(_localFileDataDescriptor != null);
return _localFileDataDescriptor.UncompressedSize;
}
else
{
return _localFileHeader.UncompressedSize;
}
}
}
internal DeflateOptionEnum DeflateOption
{
get
{
CheckDisposed();
return _localFileHeader.DeflateOption;
}
}
internal string FileName
{
get
{
CheckDisposed();
return _localFileHeader.FileName;
}
}
internal bool FolderFlag
{
get
{
CheckDisposed();
return _folderFlag;
}
}
internal bool VolumeLabelFlag
{
get
{
CheckDisposed();
return _volumeLabelFlag;
}
}
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
/// <summary>
/// Dispose(bool)
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// multiple calls are fine - just ignore them
if (!_disposedFlag)
{
try
{
// close all the public streams that have been exposed
CloseExposedStreams();
_crcCalculatingStream.Close();
if (_deflateStream != null)
_deflateStream.Close();
_fileItemStream.Close();
}
finally
{
_disposedFlag = true;
_crcCalculatingStream = null;
_deflateStream = null;
_fileItemStream = null;
}
}
}
}
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
private ZipIOLocalFileBlock(ZipIOBlockManager blockManager,
bool folderFlag,
bool volumeLabelFlag)
{
_blockManager = blockManager;
_folderFlag = folderFlag;
_volumeLabelFlag = volumeLabelFlag;
}
private void ParseRecord (BinaryReader reader,
string fileName,
long position,
ZipIOCentralDirectoryBlock centralDir,
ZipIOCentralDirectoryFileHeader centralDirFileHeader)
{
CheckDisposed();
Debug.Assert(!_blockManager.Streaming, "Not legal in Streaming mode");
_localFileHeader = ZipIOLocalFileHeader.ParseRecord(reader, _blockManager.Encoding);
// Let's find out whether local file descriptor is there or not
if (_localFileHeader.StreamingCreationFlag)
{
// seek forward by the uncompressed size
_blockManager.Stream.Seek(centralDirFileHeader.CompressedSize, SeekOrigin.Current);
_localFileDataDescriptor = ZipIOLocalFileDataDescriptor.ParseRecord(reader,
centralDirFileHeader.CompressedSize,
centralDirFileHeader.UncompressedSize,
centralDirFileHeader.Crc32,
_localFileHeader.VersionNeededToExtract);
}
else
{
_localFileDataDescriptor = null;
}
_offset = position;
_dirtyFlag = false;
checked
{
_fileItemStream = new ZipIOFileItemStream(_blockManager,
this,
position + _localFileHeader.Size,
centralDirFileHeader.CompressedSize);
}
// init deflate stream if necessary
if ((CompressionMethodEnum)_localFileHeader.CompressionMethod == CompressionMethodEnum.Deflated)
{
Debug.Assert(_fileItemStream.Position == 0, "CompressStream assumes base stream is at position zero");
_deflateStream = new CompressStream(_fileItemStream, centralDirFileHeader.UncompressedSize);
_crcCalculatingStream = new ProgressiveCrcCalculatingStream(_blockManager, _deflateStream, Crc32);
}
else if ((CompressionMethodEnum)_localFileHeader.CompressionMethod == CompressionMethodEnum.Stored)
{
_crcCalculatingStream = new ProgressiveCrcCalculatingStream(_blockManager, _fileItemStream, Crc32);
}
else
{
throw new NotSupportedException(SR.Get(SRID.ZipNotSupportedCompressionMethod));
}
Validate(fileName, centralDir, centralDirFileHeader);
}
/// <summary>
/// Validate
/// </summary>
/// <param name="fileName">pre-trimmed and normalized filename (see ValidateNormalizeFileName)</param>
/// <param name="centralDir">central directory block</param>
/// <param name="centralDirFileHeader">file header from central directory</param>
private void Validate(string fileName,
ZipIOCentralDirectoryBlock centralDir,
ZipIOCentralDirectoryFileHeader centralDirFileHeader)
{
// check that name matches parameter in a case sensitive culture neutral way
if (0 != String.CompareOrdinal(_localFileHeader.FileName, fileName))
{
throw new FileFormatException(SR.Get(SRID.CorruptedData));
}
// compare compressed and uncompressed sizes, crc from central directory
if ((VersionNeededToExtract != centralDirFileHeader.VersionNeededToExtract) ||
(GeneralPurposeBitFlag != centralDirFileHeader.GeneralPurposeBitFlag) ||
(CompressedSize != centralDirFileHeader.CompressedSize) ||
(UncompressedSize != centralDirFileHeader.UncompressedSize) ||
(CompressionMethod != centralDirFileHeader.CompressionMethod) ||
(Crc32 != centralDirFileHeader.Crc32))
{
throw new FileFormatException(SR.Get(SRID.CorruptedData));
}
// check for read into central directory (which would indicate file corruption)
if (Offset + Size > centralDir.Offset)
throw new FileFormatException(SR.Get(SRID.CorruptedData));
// No CRC check here
// delay validating the actual CRC until it is possible to do so without additional read operations
// This is only for non-streaming mode (at this point we only support creation not consumption)
// This is to avoid the forced reading of entire stream just for CRC check
// CRC check is delegated to ProgressiveCrcCalculatingStream and CRC is only validated
// once calculated CRC is available. This implies that CRC check operation is not
// guaranteed to be performed
}
static private void CheckFileAccessParameter(Stream stream, FileAccess access)
{
switch(access)
{
case FileAccess.Read:
if (!stream.CanRead)
{
throw new ArgumentException (SR.Get(SRID.CanNotReadInWriteOnlyMode));
}
break;
case FileAccess.Write:
if (!stream.CanWrite)
{
throw new ArgumentException (SR.Get(SRID.CanNotWriteInReadOnlyMode));
}
break;
case FileAccess.ReadWrite:
if (!stream.CanRead || !stream.CanWrite)
{
throw new ArgumentException (SR.Get(SRID.CanNotReadWriteInReadOnlyWriteOnlyMode));
}
break;
default:
throw new ArgumentOutOfRangeException ("access");
}
}
private void RegisterExposedStream(Stream exposedStream)
{
if (_exposedPublicStreams == null)
{
_exposedPublicStreams = new ArrayList(_initialExposedPublicStreamsCollectionSize);
}
_exposedPublicStreams.Add(exposedStream);
}
private void CloseExposedStreams()
{
if (_exposedPublicStreams != null)
{
for (int i = _exposedPublicStreams.Count - 1; i >= 0; i--)
{
ZipIOModeEnforcingStream exposedStream =
(ZipIOModeEnforcingStream)_exposedPublicStreams[i];
exposedStream.Close();
}
}
}
private void FlushExposedStreams()
{
// !!! ATTENTION !!!!
// We know for a fact that ZipIoModeEnforcingStream doesn't perform any buffering and is never "dirty";
// therefore, there is no need to flush them. Enumerating and flashing those streams has some non-trivial
// perf costs. Instead, it is much cheaper to flush the CrcCalculatingStream.
// If at any point we choose to add some buffering to the ZipIoModeEnforcingStream we will have to flush
// all of the _exposedPublicStreams in this method.
_crcCalculatingStream.Flush();
// We are going to walk through the exposed streams and if we can not find any stream that isn't Disposed yet
// we will switch deflate stream into Start Mode, by doing this we will achieve 2 goals:
// 1. Relieve Memory Pressure in the Sparse Memory Stream
// 2. Close Deflate stream in the write through mode and get the tail bytes (we can only get them if
// we close Deflate stream). This way we can make the disk layout of the File Items that are closed final.
if ((_deflateStream != null) &&
(!_localFileHeader.StreamingCreationFlag))
{
if ((_exposedPublicStreams == null) ||(_exposedPublicStreams.Count == 0))
{
((CompressStream)_deflateStream).Reset();
}
}
}
private const int _initialExposedPublicStreamsCollectionSize= 5;
private ZipIOFileItemStream _fileItemStream;
private Stream _deflateStream; // may be null - only used if stream is Deflated
// _crcCalculatingStream is used to do optimal CRC calcuation when it is possible.
// This stream can wrap either _fileItemStream or _deflateStream
// For CRC to be calculated correctly, all regualar stream operations have to
// go through this stream. This means file item streams we hand out to a client
// need to be wrapped as ProgressiveCrcCalculatingStream.
// Any other operations specific to ZipIOFileItemStream or DeflateStream should
// be directed to those streams.
private ProgressiveCrcCalculatingStream _crcCalculatingStream;
private ArrayList _exposedPublicStreams;
private ZipIOLocalFileHeader _localFileHeader = null;
private bool _localFileHeaderSaved; // only used in Streaming mode
private ZipIOLocalFileDataDescriptor _localFileDataDescriptor = null;
private ZipIOBlockManager _blockManager;
private long _offset;
// This is a shallow dirtyFlag which doesn't account for the substructures
// (ModeEnforcing Streams, compression stream FileItem Stream )
// GetDirtyFlag method is supposed to be used everywhere where a
// complete (deep ; non-shallow) check for "dirty" is required;
private bool _dirtyFlag;
private bool _disposedFlag = false;
private bool _folderFlag = false;
private bool _volumeLabelFlag = false;
}
}
|