Пример #1
0
    private int WriteCentralDirectoryHeader(ZipEntry entry)
    {
      if (entry.CompressedSize < 0)
      {
        throw new ZipException("Attempt to write central directory entry with unknown csize");
      }

      if (entry.Size < 0)
      {
        throw new ZipException("Attempt to write central directory entry with unknown size");
      }

      if (entry.Crc < 0)
      {
        throw new ZipException("Attempt to write central directory entry with unknown crc");
      }

      // Write the central file header
      WriteLEInt(ZipConstants.CentralHeaderSignature);

      // Version made by
      WriteLEShort(ZipConstants.VersionMadeBy);

      // Version required to extract
      WriteLEShort(entry.Version);

      WriteLEShort(entry.Flags);

      unchecked
      {
        WriteLEShort((byte) entry.CompressionMethod);
        WriteLEInt((int) entry.DosTime);
        WriteLEInt((int) entry.Crc);
      }

      if ((entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff))
      {
        WriteLEInt(-1);
      }
      else
      {
        WriteLEInt((int) (entry.CompressedSize & 0xffffffff));
      }

      if ((entry.IsZip64Forced()) || (entry.Size >= 0xffffffff))
      {
        WriteLEInt(-1);
      }
      else
      {
        WriteLEInt((int) entry.Size);
      }

      var name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

      if (name.Length > 0xFFFF)
      {
        throw new ZipException("Entry name is too long.");
      }

      WriteLEShort(name.Length);

      // Central header extra data is different to local header version so regenerate.
      var ed = new ZipExtraData(entry.ExtraData);

      if (entry.CentralHeaderRequiresZip64)
      {
        ed.StartNewEntry();

        if ((entry.Size >= 0xffffffff) || (useZip64_ == UseZip64.On))
        {
          ed.AddLeLong(entry.Size);
        }

        if ((entry.CompressedSize >= 0xffffffff) || (useZip64_ == UseZip64.On))
        {
          ed.AddLeLong(entry.CompressedSize);
        }

        if (entry.Offset >= 0xffffffff)
        {
          ed.AddLeLong(entry.Offset);
        }

        // Number of disk on which this file starts isnt supported and is never written here.
        ed.AddNewEntry(1);
      }
      else
      {
        // Should have already be done when local header was added.
        ed.Delete(1);
      }

      var centralExtraData = ed.GetEntryData();

      WriteLEShort(centralExtraData.Length);
      WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0);

      WriteLEShort(0); // disk number
      WriteLEShort(0); // internal file attributes

      // External file attributes...
      if (entry.ExternalFileAttributes != -1)
      {
        WriteLEInt(entry.ExternalFileAttributes);
      }
      else
      {
        WriteLEUint(entry.IsDirectory ? (uint) 16 : 0);
      }

      if (entry.Offset >= 0xffffffff)
      {
        WriteLEUint(0xffffffff);
      }
      else
      {
        WriteLEUint((uint) (int) entry.Offset);
      }

      if (name.Length > 0)
      {
        baseStream_.Write(name, 0, name.Length);
      }

      if (centralExtraData.Length > 0)
      {
        baseStream_.Write(centralExtraData, 0, centralExtraData.Length);
      }

      var rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : new byte[0];

      if (rawComment.Length > 0)
      {
        baseStream_.Write(rawComment, 0, rawComment.Length);
      }

      return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length;
    }
Пример #2
0
    /// <summary>
    ///   Finishes the stream.  This will write the central directory at the
    ///   end of the zip file and flush the stream.
    /// </summary>
    /// <remarks>
    ///   This is automatically called when the stream is closed.
    /// </remarks>
    /// <exception cref="System.IO.IOException">
    ///   An I/O error occurs.
    /// </exception>
    /// <exception cref="ZipException">
    ///   Comment exceeds the maximum length<br />
    ///   Entry name exceeds the maximum length
    /// </exception>
    public override void Finish()
    {
      if (entries == null)
      {
        return;
      }

      if (curEntry != null)
      {
        CloseEntry();
      }

      long numEntries = entries.Count;
      long sizeEntries = 0;

      foreach (ZipEntry entry in entries)
      {
        WriteLeInt(ZipConstants.CentralHeaderSignature);
        WriteLeShort(ZipConstants.VersionMadeBy);
        WriteLeShort(entry.Version);
        WriteLeShort(entry.Flags);
        WriteLeShort((short) entry.CompressionMethod);
        WriteLeInt((int) entry.DosTime);
        WriteLeInt((int) entry.Crc);

        if (entry.IsZip64Forced() ||
            (entry.CompressedSize >= uint.MaxValue))
        {
          WriteLeInt(-1);
        }
        else
        {
          WriteLeInt((int) entry.CompressedSize);
        }

        if (entry.IsZip64Forced() ||
            (entry.Size >= uint.MaxValue))
        {
          WriteLeInt(-1);
        }
        else
        {
          WriteLeInt((int) entry.Size);
        }

        var name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

        if (name.Length > 0xffff)
        {
          throw new ZipException("Name too long.");
        }

        var ed = new ZipExtraData(entry.ExtraData);

        if (entry.CentralHeaderRequiresZip64)
        {
          ed.StartNewEntry();
          if (entry.IsZip64Forced() ||
              (entry.Size >= 0xffffffff))
          {
            ed.AddLeLong(entry.Size);
          }

          if (entry.IsZip64Forced() ||
              (entry.CompressedSize >= 0xffffffff))
          {
            ed.AddLeLong(entry.CompressedSize);
          }

          if (entry.Offset >= 0xffffffff)
          {
            ed.AddLeLong(entry.Offset);
          }

          ed.AddNewEntry(1);
        }
        else
        {
          ed.Delete(1);
        }

        var extra = ed.GetEntryData();

        var entryComment =
          (entry.Comment != null)
            ? ZipConstants.ConvertToArray(entry.Flags, entry.Comment)
            : new byte[0];

        if (entryComment.Length > 0xffff)
        {
          throw new ZipException("Comment too long.");
        }

        WriteLeShort(name.Length);
        WriteLeShort(extra.Length);
        WriteLeShort(entryComment.Length);
        WriteLeShort(0); // disk number
        WriteLeShort(0); // internal file attributes
        // external file attributes

        if (entry.ExternalFileAttributes != -1)
        {
          WriteLeInt(entry.ExternalFileAttributes);
        }
        else
        {
          WriteLeInt(entry.IsDirectory ? 16 : 0);
        }

        if (entry.Offset >= uint.MaxValue)
        {
          WriteLeInt(-1);
        }
        else
        {
          WriteLeInt((int) entry.Offset);
        }

        if (name.Length > 0)
        {
          baseOutputStream_.Write(name, 0, name.Length);
        }

        if (extra.Length > 0)
        {
          baseOutputStream_.Write(extra, 0, extra.Length);
        }

        if (entryComment.Length > 0)
        {
          baseOutputStream_.Write(entryComment, 0, entryComment.Length);
        }

        sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
      }

      using (var zhs = new ZipHelperStream(baseOutputStream_))
      {
        zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment);
      }

      entries = null;
    }
Пример #3
0
    private void WriteLocalEntryHeader(ZipUpdate update)
    {
      var entry = update.OutEntry;

      // TODO: Local offset will require adjusting for multi-disk zip files.
      entry.Offset = baseStream_.Position;

      // TODO: Need to clear any entry flags that dont make sense or throw an exception here.
      if (update.Command != UpdateCommand.Copy)
      {
        if (entry.CompressionMethod == CompressionMethod.Deflated)
        {
          if (entry.Size == 0)
          {
            // No need to compress - no data.
            entry.CompressedSize = entry.Size;
            entry.Crc = 0;
            entry.CompressionMethod = CompressionMethod.Stored;
          }
        }
        else if (entry.CompressionMethod == CompressionMethod.Stored)
        {
          entry.Flags &= ~(int) GeneralBitFlags.Descriptor;
        }

        switch (useZip64_)
        {
          case UseZip64.Dynamic:
            if (entry.Size < 0)
            {
              entry.ForceZip64();
            }
            break;

          case UseZip64.On:
            entry.ForceZip64();
            break;

          case UseZip64.Off:
            // Do nothing.  The entry itself may be using Zip64 independantly.
            break;
        }
      }

      // Write the local file header
      WriteLEInt(ZipConstants.LocalHeaderSignature);

      WriteLEShort(entry.Version);
      WriteLEShort(entry.Flags);

      WriteLEShort((byte) entry.CompressionMethod);
      WriteLEInt((int) entry.DosTime);

      if (!entry.HasCrc)
      {
        // Note patch address for updating CRC later.
        update.CrcPatchOffset = baseStream_.Position;
        WriteLEInt(0);
      }
      else
      {
        WriteLEInt(unchecked((int) entry.Crc));
      }

      if (entry.LocalHeaderRequiresZip64)
      {
        WriteLEInt(-1);
        WriteLEInt(-1);
      }
      else
      {
        if ((entry.CompressedSize < 0) || (entry.Size < 0))
        {
          update.SizePatchOffset = baseStream_.Position;
        }

        WriteLEInt((int) entry.CompressedSize);
        WriteLEInt((int) entry.Size);
      }

      var name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

      if (name.Length > 0xFFFF)
      {
        throw new ZipException("Entry name too long.");
      }

      var ed = new ZipExtraData(entry.ExtraData);

      if (entry.LocalHeaderRequiresZip64)
      {
        ed.StartNewEntry();

        // Local entry header always includes size and compressed size.
        // NOTE the order of these fields is reversed when compared to the normal headers!
        ed.AddLeLong(entry.Size);
        ed.AddLeLong(entry.CompressedSize);
        ed.AddNewEntry(1);
      }
      else
      {
        ed.Delete(1);
      }

      entry.ExtraData = ed.GetEntryData();

      WriteLEShort(name.Length);
      WriteLEShort(entry.ExtraData.Length);

      if (name.Length > 0)
      {
        baseStream_.Write(name, 0, name.Length);
      }

      if (entry.LocalHeaderRequiresZip64)
      {
        if (!ed.Find(1))
        {
          throw new ZipException("Internal error cannot find extra data");
        }

        update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex;
      }

      if (entry.ExtraData.Length > 0)
      {
        baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length);
      }
    }
Пример #4
0
    /// <summary>
    ///   Starts a new Zip entry. It automatically closes the previous
    ///   entry if present.
    ///   All entry elements bar name are optional, but must be correct if present.
    ///   If the compression method is stored and the output is not patchable
    ///   the compression for that entry is automatically changed to deflate level 0
    /// </summary>
    /// <param name="entry">
    ///   the entry.
    /// </param>
    /// <exception cref="System.ArgumentNullException">
    ///   if entry passed is null.
    /// </exception>
    /// <exception cref="System.IO.IOException">
    ///   if an I/O error occured.
    /// </exception>
    /// <exception cref="System.InvalidOperationException">
    ///   if stream was finished
    /// </exception>
    /// <exception cref="ZipException">
    ///   Too many entries in the Zip file<br />
    ///   Entry name is too long<br />
    ///   Finish has already been called<br />
    /// </exception>
    public void PutNextEntry(ZipEntry entry)
    {
      if (entry == null)
      {
        throw new ArgumentNullException("entry");
      }

      if (entries == null)
      {
        throw new InvalidOperationException("ZipOutputStream was finished");
      }

      if (curEntry != null)
      {
        CloseEntry();
      }

      if (entries.Count == int.MaxValue)
      {
        throw new ZipException("Too many entries for Zip file");
      }

      var method = entry.CompressionMethod;
      var compressionLevel = defaultCompressionLevel;

      // Clear flags that the library manages internally
      entry.Flags &= (int) GeneralBitFlags.UnicodeText;
      patchEntryHeader = false;

      bool headerInfoAvailable;

      // No need to compress - definitely no data.
      if (entry.Size == 0)
      {
        entry.CompressedSize = entry.Size;
        entry.Crc = 0;
        method = CompressionMethod.Stored;
        headerInfoAvailable = true;
      }
      else
      {
        headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc;

        // Switch to deflation if storing isnt possible.
        if (method == CompressionMethod.Stored)
        {
          if (!headerInfoAvailable)
          {
            if (!CanPatchEntries)
            {
              // Can't patch entries so storing is not possible.
              method = CompressionMethod.Deflated;
              compressionLevel = 0;
            }
          }
          else // entry.size must be > 0
          {
            entry.CompressedSize = entry.Size;
            headerInfoAvailable = entry.HasCrc;
          }
        }
      }

      if (headerInfoAvailable == false)
      {
        if (CanPatchEntries == false)
        {
          // Only way to record size and compressed size is to append a data descriptor
          // after compressed data.

          // Stored entries of this form have already been converted to deflating.
          entry.Flags |= 8;
        }
        else
        {
          patchEntryHeader = true;
        }
      }

      entry.Offset = offset;
      entry.CompressionMethod = method;

      curMethod = method;
      sizePatchPos = -1;

      if ((useZip64_ == UseZip64.On) || ((entry.Size < 0) && (useZip64_ == UseZip64.Dynamic)))
      {
        entry.ForceZip64();
      }

      // Write the local file header
      WriteLeInt(ZipConstants.LocalHeaderSignature);

      WriteLeShort(entry.Version);
      WriteLeShort(entry.Flags);
      WriteLeShort((byte) method);
      WriteLeInt((int) entry.DosTime);

      // TODO: Refactor header writing.  Its done in several places.
      if (headerInfoAvailable)
      {
        WriteLeInt((int) entry.Crc);
        if (entry.LocalHeaderRequiresZip64)
        {
          WriteLeInt(-1);
          WriteLeInt(-1);
        }
        else
        {
          WriteLeInt((int) entry.CompressedSize);
          WriteLeInt((int) entry.Size);
        }
      }
      else
      {
        if (patchEntryHeader)
        {
          crcPatchPos = baseOutputStream_.Position;
        }
        WriteLeInt(0); // Crc

        if (patchEntryHeader)
        {
          sizePatchPos = baseOutputStream_.Position;
        }

        // For local header both sizes appear in Zip64 Extended Information
        if (entry.LocalHeaderRequiresZip64 || patchEntryHeader)
        {
          WriteLeInt(-1);
          WriteLeInt(-1);
        }
        else
        {
          WriteLeInt(0); // Compressed size
          WriteLeInt(0); // Uncompressed size
        }
      }

      var name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

      if (name.Length > 0xFFFF)
      {
        throw new ZipException("Entry name too long.");
      }

      var ed = new ZipExtraData(entry.ExtraData);

      if (entry.LocalHeaderRequiresZip64)
      {
        ed.StartNewEntry();
        if (headerInfoAvailable)
        {
          ed.AddLeLong(entry.Size);
          ed.AddLeLong(entry.CompressedSize);
        }
        else
        {
          ed.AddLeLong(-1);
          ed.AddLeLong(-1);
        }
        ed.AddNewEntry(1);

        if (!ed.Find(1))
        {
          throw new ZipException("Internal error cant find extra data");
        }

        if (patchEntryHeader)
        {
          sizePatchPos = ed.CurrentReadIndex;
        }
      }
      else
      {
        ed.Delete(1);
      }

      var extra = ed.GetEntryData();

      WriteLeShort(name.Length);
      WriteLeShort(extra.Length);

      if (name.Length > 0)
      {
        baseOutputStream_.Write(name, 0, name.Length);
      }

      if (entry.LocalHeaderRequiresZip64 && patchEntryHeader)
      {
        sizePatchPos += baseOutputStream_.Position;
      }

      if (extra.Length > 0)
      {
        baseOutputStream_.Write(extra, 0, extra.Length);
      }

      offset += ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;

      // Activate the entry.
      curEntry = entry;
      crc.Reset();
      if (method == CompressionMethod.Deflated)
      {
        deflater_.Reset();
        deflater_.SetLevel(compressionLevel);
      }
      size = 0;
    }