//------------------------------------------------------ // // 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); }
private void UpdateFromLocalFileBlock(ZipIOLocalFileBlock fileBlock) { Debug.Assert(DiskNumberStart == 0); _signature = _signatureConstant; _generalPurposeBitFlag = fileBlock.GeneralPurposeBitFlag; _compressionMethod = (UInt16)fileBlock.CompressionMethod; _lastModFileDateTime = fileBlock.LastModFileDateTime; _crc32 = fileBlock.Crc32; // file name is easy to copy _fileNameLength = (UInt16)fileBlock.FileName.Length; // this is safe cast as file name is always validate for size _fileName = _encoding.GetBytes(fileBlock.FileName); _stringFileName = fileBlock.FileName; // this will properly update the 32 or zip 64 fields UpdateZip64Structures(fileBlock.CompressedSize, fileBlock.UncompressedSize, fileBlock.Offset); // Previous instruction may determine that we don't really need 4.5, but we // want to ensure that the version is identical with what is stored in the local file header. Debug.Assert(_versionNeededToExtract <= fileBlock.VersionNeededToExtract, "Should never be making this smaller"); _versionNeededToExtract = fileBlock.VersionNeededToExtract; // These fields are intentionally ignored, as they are not present in the local header //_fileCommentLength; //_fileComment; //_diskNumberStart; //_internalFileAttributes; //_externalFileAttributes; }
//------------------------------------------------------ // Internal NON API Constructor (this constructor is marked as internal // and isNOT part of the ZIP IO API surface) // It supposed to be called only by the ZipArchive class //------------------------------------------------------ internal ZipFileInfo(ZipArchive zipArchive, ZipIOLocalFileBlock fileBlock) { Debug.Assert((fileBlock != null) && (zipArchive != null)); _fileBlock = fileBlock; _zipArchive = zipArchive; #if DEBUG // validate that date time is legal DateTime dt = LastModFileDateTime; #endif }
internal void AddFileBlock(ZipIOLocalFileBlock fileBlock) { _dirtyFlag = true; ZipIOCentralDirectoryFileHeader fileHeader = ZipIOCentralDirectoryFileHeader.CreateNew (_blockManager.Encoding, fileBlock); CentralDirectoryDictionary.Add(fileHeader.FileName, fileHeader); }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// Constructor - streaming and non-streaming mode /// </summary> /// <param name="baseStream"></param> /// <param name="access"></param> /// <param name="blockManager">block manager</param> /// <param name="block">associated block</param> internal ZipIOModeEnforcingStream(Stream baseStream, FileAccess access, ZipIOBlockManager blockManager, ZipIOLocalFileBlock block) { Debug.Assert(baseStream != null); _baseStream = baseStream; _access = access; _blockManager = blockManager; _block = block; }
internal bool UpdateIfNeeded(ZipIOLocalFileBlock fileBlock) { if (CheckIfUpdateNeeded(fileBlock)) { UpdateFromLocalFileBlock(fileBlock); return(true); } else { return(false); } }
public void UpdateReferences(bool closingFlag) { // we just need to ask Block Manager for the new Values for each header // there are 2 distinct cases here // 1. local file data is mapped . loaded and might have been changed in size and position // 2. local file data is not loaded and might have been changed only in position (not in size) foreach (IZipIOBlock block in _blockManager) { ZipIOLocalFileBlock localFileBlock = block as ZipIOLocalFileBlock; ZipIORawDataFileBlock rawDataFileBlock = block as ZipIORawDataFileBlock; if (localFileBlock != null) { // this is case 1 data is mapped and loaded, so we only need to find the matching // Centraldirectory record and update it Debug.Assert(CentralDirectoryDictionary.Contains(localFileBlock.FileName)); ZipIOCentralDirectoryFileHeader centralDirFileHeader = (ZipIOCentralDirectoryFileHeader)CentralDirectoryDictionary[localFileBlock.FileName]; if (centralDirFileHeader.UpdateIfNeeded(localFileBlock)) { //update was required let's mark ourselves as dirty _dirtyFlag = true; } } //check whether we deal with raw data block and it was moved else if (rawDataFileBlock != null) { long diskImageShift = rawDataFileBlock.DiskImageShift; if (diskImageShift != 0) { //this is case #2 data isn't loaded based on the shift in the RawData Block // we need to move all overlapping central directory references foreach (ZipIOCentralDirectoryFileHeader centralDirFileHeader in CentralDirectoryDictionary.Values) { // check whether central dir header points into the region of a moved RawDataBlock if (rawDataFileBlock.DiskImageContains(centralDirFileHeader.OffsetOfLocalHeader)) { centralDirFileHeader.MoveReference(diskImageShift); _dirtyFlag = true; } } } } } }
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 static ZipIOCentralDirectoryFileHeader CreateNew(Encoding encoding, ZipIOLocalFileBlock fileBlock) { ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding); // initialize fields that are not duplicated in the local file block(header) header._fileCommentLength =0; header._fileComment = null; header._diskNumberStart = 0; header._internalFileAttributes = 0; header._externalFileAttributes = 0; header._versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat; header._extraField = ZipIOExtraField.CreateNew(false /* no padding */); // update the rest of the fields based on the local file header header.UpdateFromLocalFileBlock(fileBlock); return header; }
internal static ZipIOCentralDirectoryFileHeader CreateNew(Encoding encoding, ZipIOLocalFileBlock fileBlock) { ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding); // initialize fields that are not duplicated in the local file block(header) header._fileCommentLength = 0; header._fileComment = null; header._diskNumberStart = 0; header._internalFileAttributes = 0; header._externalFileAttributes = 0; header._versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat; header._extraField = ZipIOExtraField.CreateNew(false /* no padding */); // update the rest of the fields based on the local file header header.UpdateFromLocalFileBlock(fileBlock); return(header); }
/// <summary> /// This method will result in a complete parsing of the EndOfCentralDirectory /// and CentralDirectory records (if it hasn't been done yet). /// After that (assuming no duplicates were found). It will create in appropriate /// in memory Local FileHeaders and Central Directory Headers. /// </summary> internal ZipFileInfo AddFile(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption) { CheckDisposed(); if (_openAccess == FileAccess.Read) { throw new InvalidOperationException(SR.Get(SRID.CanNotWriteInReadOnlyMode)); } // Validate parameteres zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName); if ((compressionMethod != CompressionMethodEnum.Stored) && (compressionMethod != CompressionMethodEnum.Deflated)) { throw new ArgumentOutOfRangeException("compressionMethod"); } // non-contiguous range requires more complex test if (deflateOption < DeflateOptionEnum.Normal || ( deflateOption > DeflateOptionEnum.SuperFast && deflateOption != DeflateOptionEnum.None)) { throw new ArgumentOutOfRangeException("deflateOption"); } // Check for duplicates , if (FileExists(zipFileName)) { throw new System.InvalidOperationException(SR.Get(SRID.AttemptedToCreateDuplicateFileName)); } // Create Local File Block through Block Manager ZipIOLocalFileBlock fileBlock = _blockManager.CreateLocalFileBlock(zipFileName, compressionMethod, deflateOption); //build new ZipFileInfo and add reference to the collection, so we can keep track of the instances of the ZipFileInfo, // that were given out and invalidate any collection that was returned on GetFiles calls ZipFileInfo zipFileInfo = new ZipFileInfo(this, fileBlock); ZipFileInfoDictionary.Add(zipFileInfo.Name, zipFileInfo); return(zipFileInfo); }
///////////////////////////// // Internal Constructor ///////////////////////////// internal ZipIOFileItemStream(ZipIOBlockManager blockManager, // blockManager is only needed // to pass through to it Flush requests ZipIOLocalFileBlock block, // our owning block - needed for Streaming scenarios long persistedOffset, // to map to the stream long persistedSize) // to map to the stream ) { Debug.Assert(blockManager != null); Debug.Assert(persistedOffset >= 0); Debug.Assert(persistedSize >= 0); Debug.Assert(block != null); _persistedOffset = persistedOffset; _offset = persistedOffset; _persistedSize = persistedSize; _blockManager = blockManager; _block = block; _currentStreamLength = persistedSize; }
private bool CheckIfUpdateNeeded(ZipIOLocalFileBlock fileBlock) { // there is a special case for the _generalPurposeBitFlag.Bit #3 // it could be set in the local file header indicating streaming // creation, while it doesn't need to be set in the Central directory // so having // (fileBlock.GeneralPurposeBitFlag == 8 && and _generalPurposeBitFlag == 0) // is a valid case when update is not required // let's compare the 3rd bit of the general purpose bit flag bool localFileHeaderStreamingFlag = (0 != (fileBlock.GeneralPurposeBitFlag & _streamingBitMask)); bool centralDirStreamingFlag = (0 != (_generalPurposeBitFlag & _streamingBitMask)); if (!localFileHeaderStreamingFlag && centralDirStreamingFlag) { // the mismatch if local file header in non streaming but the central directory is in streaming mode // all the other combinations do not require an update and valid as is // this includes scenario when local file header is in streaming and central dir is not return(true); } Debug.Assert(String.CompareOrdinal(_stringFileName, fileBlock.FileName) == 0); return ((_signature != _signatureConstant) || (_versionNeededToExtract != fileBlock.VersionNeededToExtract) || (_generalPurposeBitFlag != fileBlock.GeneralPurposeBitFlag) || (_compressionMethod != (UInt16)fileBlock.CompressionMethod) || (_crc32 != fileBlock.Crc32) || (CompressedSize != fileBlock.CompressedSize) || (UncompressedSize != fileBlock.UncompressedSize) || (OffsetOfLocalHeader != fileBlock.Offset)); // These fields are intentionally ignored, as they are not present in the local header //_fileCommentLength; //_fileComment; //_diskNumberStart; //_internalFileAttributes; //_externalFileAttributes; }
/// <summary> /// This method will result in a complete parsing of the EndOfCentralDirectory /// and CentralDirectory records (if it hasn't been done yet). /// After that (assuming the file was found). It will parse the apropriate local file block /// header and data descriptor (if present). /// </summary> internal ZipFileInfo GetFile(string zipFileName) { CheckDisposed(); if (_openAccess == FileAccess.Write) { throw new InvalidOperationException(SR.Get(SRID.CanNotReadInWriteOnlyMode)); } // Validate parameteres zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName); // try to get it from the ZipFileInfo dictionary if (ZipFileInfoDictionary.Contains(zipFileName)) { // this ZipFileInfo was already built through AddFile or GetFile(s) // we have this cached return((ZipFileInfo)(ZipFileInfoDictionary[zipFileName])); } else { // we need to check whether it is present in the central directory if (!FileExists(zipFileName)) { throw new InvalidOperationException(SR.Get(SRID.FileDoesNotExists)); } // Load Local File Block through Block Manager ZipIOLocalFileBlock fileBlock = _blockManager.LoadLocalFileBlock(zipFileName); // build new ZipFileInfo and add reference to the collection, so we can keep track of the instances of the ZipFileInfo, // that were given out and invalidate any collection that was returned on GetFiles calls ZipFileInfo zipFileInfo = new ZipFileInfo(this, fileBlock); //this should invalidate any outstanding collections ZipFileInfoDictionary.Add(zipFileInfo.Name, zipFileInfo); return(zipFileInfo); } }
//----------------------------------------------------- // Internal NON API Constructor (this constructor is marked as internal // and isNOT part of the ZIP IO API surface) // It supposed to be called only by the ZipArchive class //------------------------------------------------------ internal ZipFileInfo(ZipArchive zipArchive, ZipIOLocalFileBlock fileBlock) { Debug.Assert((fileBlock != null) && (zipArchive != null)); _fileBlock = fileBlock; _zipArchive = zipArchive; #if DEBUG // validate that date time is legal DateTime dt = LastModFileDateTime; #endif }
internal void RemoveLocalFileBlock(ZipIOLocalFileBlock localFileBlock) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); Debug.Assert(localFileBlock != null, " At this point local File block must be preloaded"); Debug.Assert(CentralDirectoryBlock.FileExists(localFileBlock.FileName), " At this point local File block must be mapped in central directory"); // remove it from our list _blockList.Remove(localFileBlock); // remove this from Central Directory CentralDirectoryBlock.RemoveFileBlock(localFileBlock.FileName); DirtyFlag = true; // at this point we can Dispose it to make sure that any calls // to this file block through outstanding indirect references will result in object Disposed exception localFileBlock.Dispose(); }
private bool CheckIfUpdateNeeded(ZipIOLocalFileBlock fileBlock) { // there is a special case for the _generalPurposeBitFlag.Bit #3 // it could be set in the local file header indicating streaming // creation, while it doesn't need to be set in the Central directory // so having // (fileBlock.GeneralPurposeBitFlag == 8 && and _generalPurposeBitFlag == 0) // is a valid case when update is not required // let's compare the 3rd bit of the general purpose bit flag bool localFileHeaderStreamingFlag = (0 != (fileBlock.GeneralPurposeBitFlag & _streamingBitMask)); bool centralDirStreamingFlag = (0 != (_generalPurposeBitFlag & _streamingBitMask)); if (!localFileHeaderStreamingFlag && centralDirStreamingFlag) { // the mismatch if local file header in non streaming but the central directory is in streaming mode // all the other combinations do not require an update and valid as is // this includes scenario when local file header is in streaming and central dir is not return true; } Debug.Assert(String.CompareOrdinal(_stringFileName, fileBlock.FileName) == 0); return (_signature != _signatureConstant) || (_versionNeededToExtract != fileBlock.VersionNeededToExtract) || (_generalPurposeBitFlag != fileBlock.GeneralPurposeBitFlag) || (_compressionMethod != (UInt16)fileBlock.CompressionMethod) || (_crc32 != fileBlock.Crc32) || (CompressedSize != fileBlock.CompressedSize) || (UncompressedSize != fileBlock.UncompressedSize) || (OffsetOfLocalHeader != fileBlock.Offset); // These fields are intentionally ignored, as they are not present in the local header //_fileCommentLength; //_fileComment; //_diskNumberStart; //_internalFileAttributes; //_externalFileAttributes; }
/// <summary> /// Save - stream level /// </summary> /// <param name="blockRequestingFlush"></param> /// <param name="closingFlag">closing or flushing</param> internal void SaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // Prevent recursion when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { // redirect depending on our mode if (_openStreaming) { StreamingSaveStream(blockRequestingFlush, closingFlag); } else SaveContainer(false); } finally { // all done so restore state _propagatingFlushDisposed = false; } }
internal bool UpdateIfNeeded(ZipIOLocalFileBlock fileBlock) { if (CheckIfUpdateNeeded(fileBlock)) { UpdateFromLocalFileBlock(fileBlock); return true; } else { return false; } }
///////////////////////////// // Internal Constructor ///////////////////////////// internal ZipIOFileItemStream(ZipIOBlockManager blockManager, // blockManager is only needed // to pass through to it Flush requests ZipIOLocalFileBlock block, // our owning block - needed for Streaming scenarios long persistedOffset, // to map to the stream long persistedSize) // to map to the stream ) { Debug.Assert(blockManager != null); Debug.Assert(persistedOffset >=0); Debug.Assert(persistedSize >= 0); Debug.Assert(block != null); _persistedOffset = persistedOffset; _offset = persistedOffset; _persistedSize = persistedSize; _blockManager = blockManager; _block = block; _currentStreamLength = persistedSize; }
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; }
/// <summary> /// Flush was called on a ZipIOFileItemStream /// </summary> /// <param name="blockRequestingFlush">block that owns the stream that Flush was called on</param> /// <param name="closingFlag">close or dispose</param> private void StreamingSaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // STREAMING MODE: // Flush will do one of two things, depending on the currently open stream: // 1) If the currently open stream matches the one passed (or none is currently opened) // then write will occur to the open stream. // 2) Otherwise, the currently opened stream will be flushed and closed, and the // given stream will become the currently opened stream // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) // different stream? if (_streamingCurrentlyOpenStreamBlock != blockRequestingFlush) { // need to close the currently opened stream // unless its our first time through if (_streamingCurrentlyOpenStreamBlock != null) { _streamingCurrentlyOpenStreamBlock.SaveStreaming(true); } // Now make the given stream the new "currently opened stream". _streamingCurrentlyOpenStreamBlock = blockRequestingFlush; } // this should now be flushable/closable _streamingCurrentlyOpenStreamBlock.SaveStreaming(closingFlag); // if closing - discard the stream because it is now closed if (closingFlag) _streamingCurrentlyOpenStreamBlock = null; }
//----------------------------------------------------- // // 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; }