예제 #1
0
        /// <summary>
        /// Gets the offset of an archive that is positioned 0 or more bytes
        /// from the start of the Stream.
        /// </summary>
        /// <param name="stream">A stream for reading the archive.</param>
        /// <returns>The offset in bytes of the archive,
        /// or -1 if no archive is found in the Stream.</returns>
        /// <remarks>The archive must begin on a 4-byte boundary.</remarks>
        public override long FindArchiveOffset(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            long offset = base.FindArchiveOffset(stream);

            if (offset > 0)
            {
                // Some self-extract packages include the exe stub in file offset calculations.
                // Check the first header directory offset to decide whether the entire
                // archive needs to be offset or not.

                ZipEndOfCentralDirectory eocd = this.GetEOCD(null, stream);
                if (eocd != null && eocd.totalEntries > 0)
                {
                    stream.Seek(eocd.dirOffset, SeekOrigin.Begin);

                    ZipFileHeader header = new ZipFileHeader();
                    if (header.Read(stream, true) && header.localHeaderOffset < stream.Length)
                    {
                        stream.Seek(header.localHeaderOffset, SeekOrigin.Begin);
                        if (header.Read(stream, false))
                        {
                            return(0);
                        }
                    }
                }
            }

            return(offset);
        }
예제 #2
0
        /// <summary>
        /// Creates a zip archive or chain of zip archives.
        /// </summary>
        /// <param name="streamContext">A context interface to handle opening
        /// and closing of archive and file streams.</param>
        /// <param name="files">An array of file lists.  Each list is
        /// compressed into one stream in the archive.</param>
        /// <param name="maxArchiveSize">The maximum number of bytes for one archive
        /// before the contents are chained to the next archive, or zero for unlimited
        /// archive size.</param>
        /// <exception cref="ArchiveException">The archive could not be
        /// created.</exception>
        /// <remarks>
        /// The stream context implementation may provide a mapping from the file
        /// paths within the archive to the external file paths.
        /// </remarks>
        public override void Pack(
            IPackStreamContext streamContext,
            IEnumerable <string> files,
            long maxArchiveSize)
        {
            if (streamContext == null)
            {
                throw new ArgumentNullException("streamContext");
            }

            if (files == null)
            {
                throw new ArgumentNullException("files");
            }

            lock (this)
            {
                Stream archiveStream = null;
                try
                {
                    this.ResetProgressData();
                    this.totalArchives = 1;

                    object forceZip64Value = streamContext.GetOption("forceZip64", null);
                    bool   forceZip64      = Convert.ToBoolean(
                        forceZip64Value, CultureInfo.InvariantCulture);

                    // Count the total number of files and bytes to be compressed.
                    foreach (string file in files)
                    {
                        FileAttributes attributes;
                        DateTime       lastWriteTime;
                        Stream         fileStream = streamContext.OpenFileReadStream(
                            file,
                            out attributes,
                            out lastWriteTime);
                        if (fileStream != null)
                        {
                            this.totalFileBytes += fileStream.Length;
                            this.totalFiles++;
                            streamContext.CloseFileReadStream(file, fileStream);
                        }
                    }

                    List <ZipFileHeader> fileHeaders = new List <ZipFileHeader>();
                    this.currentFileNumber = -1;

                    if (this.currentArchiveName == null)
                    {
                        this.mainArchiveName    = streamContext.GetArchiveName(0);
                        this.currentArchiveName = this.mainArchiveName;

                        if (String.IsNullOrEmpty(this.currentArchiveName))
                        {
                            throw new FileNotFoundException("No name provided for archive.");
                        }
                    }

                    this.OnProgress(ArchiveProgressType.StartArchive);

                    // Compress files one by one, saving header info for each.
                    foreach (string file in files)
                    {
                        ZipFileHeader fileHeader = this.PackOneFile(
                            streamContext,
                            file,
                            maxArchiveSize,
                            forceZip64,
                            ref archiveStream);

                        if (fileHeader != null)
                        {
                            fileHeaders.Add(fileHeader);
                        }

                        this.currentArchiveTotalBytes = (archiveStream != null ?
                                                         archiveStream.Position : 0);
                        this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
                    }

                    bool zip64 = forceZip64 || this.totalFiles > UInt16.MaxValue;

                    // Write the central directory composed of all the file headers.
                    uint centralDirStartArchiveNumber = 0;
                    long centralDirStartPosition      = 0;
                    long centralDirSize = 0;
                    for (int i = 0; i < fileHeaders.Count; i++)
                    {
                        ZipFileHeader fileHeader = fileHeaders[i];

                        int headerSize = fileHeader.GetSize(true);
                        centralDirSize += headerSize;

                        this.CheckArchiveWriteStream(
                            streamContext,
                            maxArchiveSize,
                            headerSize,
                            ref archiveStream);

                        if (i == 0)
                        {
                            centralDirStartArchiveNumber = (uint)this.currentArchiveNumber;
                            centralDirStartPosition      = archiveStream.Position;
                        }

                        fileHeader.Write(archiveStream, true);
                        if (fileHeader.zip64)
                        {
                            zip64 = true;
                        }
                    }

                    this.currentArchiveTotalBytes =
                        (archiveStream != null ? archiveStream.Position : 0);
                    this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;

                    ZipEndOfCentralDirectory eocd = new ZipEndOfCentralDirectory();
                    eocd.dirStartDiskNumber = centralDirStartArchiveNumber;
                    eocd.entriesOnDisk      = fileHeaders.Count;
                    eocd.totalEntries       = fileHeaders.Count;
                    eocd.dirSize            = centralDirSize;
                    eocd.dirOffset          = centralDirStartPosition;
                    eocd.comment            = this.comment;

                    Zip64EndOfCentralDirectoryLocator eocdl =
                        new Zip64EndOfCentralDirectoryLocator();

                    int maxFooterSize = eocd.GetSize(false);
                    if (archiveStream != null && (zip64 || archiveStream.Position >
                                                  ((long)UInt32.MaxValue) - eocd.GetSize(false)))
                    {
                        maxFooterSize += eocd.GetSize(true) + (int)
                                         Zip64EndOfCentralDirectoryLocator.EOCDL64_SIZE;
                        zip64 = true;
                    }

                    this.CheckArchiveWriteStream(
                        streamContext,
                        maxArchiveSize,
                        maxFooterSize,
                        ref archiveStream);
                    eocd.diskNumber = (uint)this.currentArchiveNumber;

                    if (zip64)
                    {
                        eocd.versionMadeBy       = 45;
                        eocd.versionNeeded       = 45;
                        eocd.zip64               = true;
                        eocdl.dirOffset          = archiveStream.Position;
                        eocdl.dirStartDiskNumber = (uint)this.currentArchiveNumber;
                        eocdl.totalDisks         = (uint)this.currentArchiveNumber + 1;
                        eocd.Write(archiveStream);
                        eocdl.Write(archiveStream);

                        eocd.dirOffset          = UInt32.MaxValue;
                        eocd.dirStartDiskNumber = UInt16.MaxValue;
                    }

                    eocd.zip64 = false;
                    eocd.Write(archiveStream);

                    this.currentArchiveTotalBytes     = archiveStream.Position;
                    this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
                }
                finally
                {
                    if (archiveStream != null)
                    {
                        streamContext.CloseArchiveWriteStream(
                            this.currentArchiveNumber, this.mainArchiveName, archiveStream);
                        this.OnProgress(ArchiveProgressType.FinishArchive);
                    }
                }
            }
        }
예제 #3
0
        private ZipEndOfCentralDirectory GetEOCD(
            IUnpackStreamContext streamContext, Stream archiveStream)
        {
            BinaryReader reader = new BinaryReader(archiveStream);
            long         offset = archiveStream.Length
                                  - ZipEndOfCentralDirectory.EOCD_RECORD_FIXEDSIZE;

            while (offset >= 0)
            {
                archiveStream.Seek(offset, SeekOrigin.Begin);

                uint sig = reader.ReadUInt32();
                if (sig == ZipEndOfCentralDirectory.EOCDSIG)
                {
                    break;
                }

                offset--;
            }

            if (offset < 0)
            {
                return(null);
            }

            ZipEndOfCentralDirectory eocd = new ZipEndOfCentralDirectory();

            archiveStream.Seek(offset, SeekOrigin.Begin);
            if (!eocd.Read(archiveStream))
            {
                throw new ZipException("Invalid end of central directory record");
            }

            if (eocd.dirOffset == (long)UInt32.MaxValue)
            {
                string saveComment = eocd.comment;

                archiveStream.Seek(
                    offset - Zip64EndOfCentralDirectoryLocator.EOCDL64_SIZE,
                    SeekOrigin.Begin);

                Zip64EndOfCentralDirectoryLocator eocdl =
                    new Zip64EndOfCentralDirectoryLocator();
                if (!eocdl.Read(archiveStream))
                {
                    throw new ZipException("Missing or invalid end of " +
                                           "central directory record locator");
                }

                if (eocdl.dirStartDiskNumber == eocdl.totalDisks - 1)
                {
                    // ZIP64 eocd is entirely in current stream.
                    archiveStream.Seek(eocdl.dirOffset, SeekOrigin.Begin);
                    if (!eocd.Read(archiveStream))
                    {
                        throw new ZipException("Missing or invalid ZIP64 end of " +
                                               "central directory record");
                    }
                }
                else if (streamContext == null)
                {
                    return(null);
                }
                else
                {
                    // TODO: handle EOCD64 spanning archives!
                    throw new NotImplementedException("Zip implementation does not " +
                                                      "handle end of central directory record that spans archives.");
                }

                eocd.comment = saveComment;
            }

            return(eocd);
        }
예제 #4
0
        /// <summary>
        /// Reads all the file headers from the central directory in the main archive.
        /// </summary>
        private IList <ZipFileHeader> GetCentralDirectory(IUnpackStreamContext streamContext)
        {
            Stream archiveStream = null;

            this.currentArchiveNumber = 0;
            try
            {
                List <ZipFileHeader> headers = new List <ZipFileHeader>();
                archiveStream = this.OpenArchive(streamContext, 0);

                ZipEndOfCentralDirectory eocd = this.GetEOCD(streamContext, archiveStream);
                if (eocd == null)
                {
                    return(null);
                }
                else if (eocd.totalEntries == 0)
                {
                    return(headers);
                }

                headers.Capacity = (int)eocd.totalEntries;

                if (eocd.dirOffset > archiveStream.Length - ZipFileHeader.CFH_FIXEDSIZE)
                {
                    streamContext.CloseArchiveReadStream(
                        this.currentArchiveNumber, String.Empty, archiveStream);
                    archiveStream = null;
                }
                else
                {
                    archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin);
                    uint sig = new BinaryReader(archiveStream).ReadUInt32();
                    if (sig != ZipFileHeader.CFHSIG)
                    {
                        streamContext.CloseArchiveReadStream(
                            this.currentArchiveNumber, String.Empty, archiveStream);
                        archiveStream = null;
                    }
                }

                if (archiveStream == null)
                {
                    this.currentArchiveNumber = (short)(eocd.dirStartDiskNumber + 1);
                    archiveStream             = streamContext.OpenArchiveReadStream(
                        this.currentArchiveNumber, String.Empty, this);

                    if (archiveStream == null)
                    {
                        return(null);
                    }
                }

                archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin);

                while (headers.Count < eocd.totalEntries)
                {
                    ZipFileHeader header = new ZipFileHeader();
                    if (!header.Read(archiveStream, true))
                    {
                        throw new ZipException(
                                  "Missing or invalid central directory file header");
                    }

                    headers.Add(header);

                    if (headers.Count < eocd.totalEntries &&
                        archiveStream.Position == archiveStream.Length)
                    {
                        streamContext.CloseArchiveReadStream(
                            this.currentArchiveNumber, String.Empty, archiveStream);
                        this.currentArchiveNumber++;
                        archiveStream = streamContext.OpenArchiveReadStream(
                            this.currentArchiveNumber, String.Empty, this);
                        if (archiveStream == null)
                        {
                            this.currentArchiveNumber = 0;
                            archiveStream             = streamContext.OpenArchiveReadStream(
                                this.currentArchiveNumber, String.Empty, this);
                        }
                    }
                }

                return(headers);
            }
            finally
            {
                if (archiveStream != null)
                {
                    streamContext.CloseArchiveReadStream(
                        this.currentArchiveNumber, String.Empty, archiveStream);
                }
            }
        }