示例#1
0
        /// <summary>
        ///  Read ZIP file directory
        /// </summary>
        /// <exception cref="UZipDotNet.Exception">
        /// ZIP file is too short
        /// or
        /// Invalid ZIP file (No central directory)
        /// or
        /// No support for multi-disk ZIP file
        /// or
        /// Central directory is empty or in error
        /// or
        /// Central directory empty
        /// or
        /// File directory signature error
        /// or
        /// Directory name must have a slash
        /// </exception>
        private void ReadZipFileDirectory()
        {
            //	End of central directory record:
            //	Pos		Len
            //	0		4		End of central directory signature = 0x06054b50
            //	4		2		Number of this disk
            //	6		2		Disk where central directory starts
            //	8		2		Number of central directory records on this disk
            //	10		2		Total number of central directory records
            //	12		4		Size of central directory (bytes)
            //	16		4		Offset of start of central directory, relative to start of archive
            //	20		2		ZIP file comment length (n)
            //	22		n		ZIP file comment
            //
            //	Central directory file header
            //
            //	Pos		Len
            //	0		4		Central directory file header signature = 0x02014b50
            //	4		2		Version made by
            //	6		2		Version needed to extract (minimum)
            //	8		2		General purpose bit flag
            //	10		2		Compression method
            //	12		2		File last modification time
            //	14		2		File last modification date
            //	16		4		CRC-32
            //	20		4		Compressed size
            //	24		4		Uncompressed size
            //	28		2		File name length (n)
            //	30		2		Extra field length (m)
            //	32		2		File comment length (k)
            //	34		2		Disk number where file starts
            //	36		2		Internal file attributes
            //	38		4		External file attributes
            //	42		4		Offset of local file header
            //	46		n		File name
            //	46+n	m		Extra field
            //	46+n+m	k		File comment

            // file length
            long fileLen = _writeStream.Length;

            if (fileLen < 98)
            {
                throw new Exception("ZIP file is too short");
            }

            // read last 512 byte block at the end of the file
            if (fileLen > 512)
            {
                _writeStream.Position = fileLen - 512;
            }
            byte[] dirSig = _writeFileReader.ReadBytes(512);

            // look for signature
            int ptr;

            for (ptr = dirSig.Length - 20; ptr >= 0 && BitConverter.ToInt32(dirSig, ptr) != 0x06054b50; ptr--)
            {
            }
            if (ptr < 0)
            {
                throw new Exception("Invalid ZIP file (No central directory)");
            }
            ptr += 4;

            // number of this disk should be zero
            short diskNo = BitConverter.ToInt16(dirSig, ptr);

            ptr += 2;
            if (diskNo != 0)
            {
                throw new Exception("No support for multi-disk ZIP file");
            }

            // disk where central directory starts should be zero
            short dirDiskNo = BitConverter.ToInt16(dirSig, ptr);

            ptr += 2;
            if (dirDiskNo != 0)
            {
                throw new Exception("No support for multi-disk ZIP file");
            }

            // Number of central directory records on this disk
            short dirEntries = BitConverter.ToInt16(dirSig, ptr);

            ptr += 2;

            // Total number of central directory records
            short dirEntriesTotal = BitConverter.ToInt16(dirSig, ptr);

            ptr += 2;
            if (dirEntriesTotal == 0 || dirEntriesTotal != dirEntries)
            {
                throw new Exception("Central directory is empty or in error");
            }

            // Size of central directory (bytes)
            int dirSize = BitConverter.ToInt32(dirSig, ptr);

            ptr += 4;
            if (dirSize == 0)
            {
                throw new Exception("Central directory empty");
            }

            // Offset of start of central directory, relative to start of archive
            _zipDirPosition = BitConverter.ToInt32(dirSig, ptr);
            if (_zipDirPosition == 0)
            {
                throw new Exception("Central directory empty");
            }

            // create new zip directory array
            ZipDir = new List <FileHeader>(dirEntriesTotal);

            // position file to central directory
            _writeStream.Position = _zipDirPosition;

            // read central directory
            while (dirEntriesTotal-- > 0)
            {
                // file header
                var fh = new FileHeader();

                // Central directory file header signature = 0x02014b50
                int fileDirSig = _writeFileReader.ReadInt32();
                if (fileDirSig != 0x02014b50)
                {
                    throw new Exception("File directory signature error");
                }

                // ReSharper disable once UnusedVariable : Version made by (ignored)
                int versionMadeBy = _writeFileReader.ReadInt16();

                // Low byte is version needed to extract (the low byte should be 20 for version 2.0).
                // High byte is a computer system code that define the extrenal file attribute.
                // If high byte is zero it is DOS compatible.
                fh.Version = _writeFileReader.ReadUInt16();

                //	General purpose bit flag (bit 0 encripted)
                fh.BitFlags = _writeFileReader.ReadUInt16();

                //	Compression method must be deflate or no compression
                fh.CompMethod = _writeFileReader.ReadUInt16();

                //	File last modification time
                fh.FileTime = _writeFileReader.ReadUInt16();

                //	File last modification date
                fh.FileDate = _writeFileReader.ReadUInt16();

                // CRC-32
                fh.FileCRC32 = _writeFileReader.ReadUInt32();

                // Compressed size
                fh.CompSize = _writeFileReader.ReadUInt32();

                // Uncompressed size
                fh.FileSize = _writeFileReader.ReadUInt32();

                // File name length
                int fileNameLen = _writeFileReader.ReadInt16();

                // Extra field length
                int extraFieldLen = _writeFileReader.ReadInt16();

                // File comment length
                int commentLen = _writeFileReader.ReadInt16();

                // Disk number where file starts
                int fileDiskNo = _writeFileReader.ReadInt16();
                if (fileDiskNo != 0)
                {
                    throw new Exception("No support for multi-disk ZIP file");
                }

                // ReSharper disable once UnusedVariable : internal file attributes (ignored)
                int fileIntAttr = _writeFileReader.ReadInt16();

                // external file attributes
                fh.FileAttr = (FileAttributes)_writeFileReader.ReadUInt32();

                // if file system is not FAT or equivalent, erase the attributes
                if ((fh.Version & 0xff00) != 0)
                {
                    fh.FileAttr = 0;
                }

                // file position
                fh.FilePos = _writeFileReader.ReadUInt32();

                // file name
                // read all the bytes of the file name into a byte array
                // extract a string from the byte array using DOS (IBM OEM code page 437)
                // replace the unix forward slash with microsoft back slash
                fh.FileName = Utils.DecodeFilename(_writeFileReader.ReadBytes(fileNameLen));

                // if file attribute is a directory make sure we have terminating backslash
                //			if((FH.FileAttr & FileAttributes.Directory) != 0 && !FH.FileName.EndsWith("\\")) FH.FileName += "\\";

                // find if file name contains a path
                fh.Path = fh.FileName.Contains(Path.DirectorySeparatorChar);

                // if we have a directory, we must have a terminating slash
                if ((fh.FileAttr & FileAttributes.Directory) != 0 && !fh.Path)
                {
                    throw new Exception("Directory name must have a slash");
                }

                // Skip Extra field and File comment
                _writeStream.Position += extraFieldLen + commentLen;

                // add file header to zip directory
                ZipDir.Add(fh);
            }

            // position file to central directory
            _writeStream.Position = _zipDirPosition;

            // remove central directory from this file
            _writeStream.SetLength(_zipDirPosition);

            // sort array
            ZipDir.Sort();
        }
示例#2
0
        /// <summary>
        /// Write ZIP file directory
        /// </summary>
        /// <exception cref="UZipDotNet.Exception">No support for files over 4GB</exception>
        private void ZipFileDirectory()
        {
            //	End of central directory record:
            //	Pos		Len
            //	0		4		End of central directory signature = 0x06054b50
            //	4		2		Number of this disk
            //	6		2		Disk where central directory starts
            //	8		2		Number of central directory records on this disk
            //	10		2		Total number of central directory records
            //	12		4		Size of central directory (bytes)
            //	16		4		Offset of start of central directory, relative to start of archive
            //	20		2		ZIP file comment length (n)
            //	22		n		ZIP file comment
            //
            //	Central directory file header
            //
            //	Pos		Len
            //	0		4		Central directory file header signature = 0x02014b50
            //	4		2		Version made by
            //	6		2		Version needed to extract (minimum)
            //	8		2		General purpose bit flag
            //	10		2		Compression method
            //	12		2		File last modification time
            //	14		2		File last modification date
            //	16		4		CRC-32
            //	20		4		Compressed size
            //	24		4		Uncompressed size
            //	28		2		File name length (n)
            //	30		2		Extra field length (m)
            //	32		2		File comment length (k)
            //	34		2		Disk number where file starts
            //	36		2		Internal file attributes
            //	38		4		External file attributes
            //	42		4		Relative offset of local file header.
            //	46		n		File name
            //	46+n	m		Extra field
            //	46+n+m	k		File comment

            // file is too long
            if (_writeStream.Length > 0xffffffff)
            {
                throw new Exception("No support for files over 4GB");
            }

            // save directory position
            uint dirPos = (uint)_writeStream.Length;

            // sort by position
            ZipDir.Sort(FileHeader.CompareByPosition);

            // write central directory at the end of the file
            for (int entryNo = 0; entryNo < ZipDir.Count; entryNo++)
            {
                // shortcut
                FileHeader fh = ZipDir[entryNo];

                // Central directory file header signature = 0x02014b50
                _writeFile.Write((UInt32)0x02014b50);

                // Version made by 2.0
                _writeFile.Write(fh.Version);

                // Low byte is version needed to extract (the low byte should be 20 for version 2.0).
                // High byte is a computer system code that define the extrenal file attribute.
                // If high byte is zero it is DOS compatible.
                _writeFile.Write(fh.Version);

                //	General purpose bit flag (bit 0 encripted)
                _writeFile.Write(fh.BitFlags);

                //	Compression method must be deflate or no compression
                _writeFile.Write(fh.CompMethod);

                //	File last modification time
                _writeFile.Write(fh.FileTime);

                //	File last modification date
                _writeFile.Write(fh.FileDate);

                // CRC-32
                _writeFile.Write(fh.FileCRC32);

                // Compressed size
                _writeFile.Write(fh.CompSize);

                // Uncompressed size
                _writeFile.Write(fh.FileSize);

                // convert filename string to array of bytes using DOS (IBM OEM code page 437) encoding
                byte[] fileName = Utils.EncodeFilename(fh.FileName);

                // File name length
                _writeFile.Write((UInt16)fileName.Length);

                // Extra field length
                _writeFile.Write((UInt16)0);

                // File comment length
                _writeFile.Write((UInt16)0);

                // Disk number where file starts
                _writeFile.Write((UInt16)0);

                // internal file attributes
                _writeFile.Write((UInt16)0);

                // external file attributes
                _writeFile.Write((UInt32)fh.FileAttr);

                // file position
                _writeFile.Write(fh.FilePos);

                // file name (Byte array)
                _writeFile.Write(fileName);
            }

            // directory size
            uint dirSize = (uint)_writeStream.Length - dirPos;

            // Central directory signature = 0x06054b50
            _writeFile.Write((UInt32)0x06054b50);

            // number of this disk should be zero
            _writeFile.Write((UInt16)0);

            // disk where central directory starts should be zero
            _writeFile.Write((UInt16)0);

            // number of central directory records on this disk
            _writeFile.Write((UInt16)ZipDir.Count);

            // Total number of central directory records
            _writeFile.Write((UInt16)ZipDir.Count);

            // Size of central directory (bytes)
            _writeFile.Write(dirSize);

            // Offset of start of central directory, relative to start of archive
            _writeFile.Write(dirPos);

            // directory comment length should be zero
            _writeFile.Write((UInt16)0);

            // sort by file name
            ZipDir.Sort();
        }
示例#3
0
        /// <summary>
        /// Delete files
        /// </summary>
        /// <exception cref="UZipDotNet.Exception">
        /// Delete file: Read file header error
        /// or
        /// Delete file: ZIP signature in error
        /// or
        /// Delete file: Read first block error
        /// or
        /// Delete file: read file block error
        /// </exception>
        private void DeleteFiles()
        {
            // allocate 64kb array
            var buffer = new byte[1024 * 64];

            // sort directory by position
            ZipDir.Sort(FileHeader.CompareByPosition);

            // new file position
            long writePosition = 0;

            // loop for all files
            for (int index = 0; index < ZipDir.Count; index++)
            {
                // short cut
                FileHeader fileHeader = ZipDir[index];

                // read position
                long readPosition = fileHeader.FilePos;

                // set file position
                _writeStream.Position = readPosition;

                // read local file header base part
                int len = _writeFileReader.Read(buffer, 0, 30);
                if (len != 30)
                {
                    throw new Exception("Delete file: Read file header error");
                }

                // local file header signature
                if (BitConverter.ToUInt32(buffer, 0) != 0x04034b50)
                {
                    throw new Exception("Delete file: ZIP signature in error");
                }

                // file name length
                uint fileNameLen = BitConverter.ToUInt16(buffer, 26);

                // extra fields length
                uint extraFieldLen = BitConverter.ToUInt16(buffer, 28);

                // total length of compressed file plus local file header
                uint totalLength = 30 + fileNameLen + extraFieldLen + fileHeader.CompSize;

                // test if move is required
                if (readPosition == writePosition)
                {
                    // no move required
                    writePosition += totalLength;
                    continue;
                }

                // change file position in the directory
                fileHeader.FilePos = (uint)writePosition;

                // fill the first buffer
                // we have already the first 30 bytes
                int blockLen = (totalLength > (uint)buffer.Length ? buffer.Length : (int)totalLength) - 30;
                len = _writeFileReader.Read(buffer, 30, blockLen);
                if (len != blockLen)
                {
                    throw new Exception("Delete file: Read first block error");
                }
                blockLen += 30;

                // update read position
                readPosition += blockLen;

                // copy loop
                for (; ;)
                {
                    // set write position
                    _writeStream.Position = writePosition;

                    // write the block
                    _writeFile.Write(buffer, 0, blockLen);

                    // update write position
                    writePosition += blockLen;

                    // update total length
                    totalLength -= (uint)blockLen;
                    if (totalLength == 0)
                    {
                        break;
                    }

                    // set position to next block
                    _writeStream.Position = readPosition;

                    // read next block
                    blockLen = totalLength > (uint)buffer.Length ? buffer.Length : (int)totalLength;
                    len      = _writeFileReader.Read(buffer, 0, blockLen);

                    if (len != blockLen)
                    {
                        throw new Exception("Delete file: read file block error");
                    }

                    // update read position
                    readPosition += blockLen;
                }
            }

            // truncate file to current write position
            _writeStream.Position = writePosition;
            _writeStream.SetLength(writePosition);
        }
示例#4
0
        /// <summary>
        /// ZIP local file header
        /// Write file header to output file with CRC and compressed length set to zero
        /// </summary>
        /// <param name="fullFileName">Full name of the file.</param>
        /// <param name="archiveFileName">Name of the archive file.</param>
        private void WriteFileHeader(string fullFileName, string archiveFileName)
        {
            //
            //	file header
            //
            //	Pos		Len
            //	0		4		Local file header signature = 0x04034b50
            //	4		2		Version needed to extract (minimum)
            //	6		2		General purpose bit flag
            //	8		2		Compression method
            //	10		2		File last modification time
            //	12		2		File last modification date
            //	14		4		CRC-32
            //	18		4		Compressed size
            //	22		4		Uncompressed size
            //	26		2		File name length (n)
            //	28		2		Extra field length (m)
            //	30		n		File name
            //	30+n	m		Extra field

            // save write file length for possible error
            _writeStartPosition = _writeStream.Length;

            // file info
            var fi = new FileInfo(fullFileName);

            // Directory flag
            bool dirFlag = (fi.Attributes & FileAttributes.Directory) != 0;

            // create file header structure (name, time, attributes and version)
            _fileHeaderInfo = new FileHeader(archiveFileName, fi.LastWriteTime, fi.Attributes, _writeStream.Length, dirFlag ? 0 : fi.Length);

            // compression method deflate
            if (!dirFlag)
            {
                _fileHeaderInfo.CompMethod = 8;
            }

            // test for duplicate file name
            if (ZipDir.BinarySearch(_fileHeaderInfo) >= 0)
            {
                throw new Exception("Duplicate file name");
            }

            // local file header signature
            _writeFile.Write((uint)0x04034b50);

            // version needed to extract 2.0 (file system is dos)
            _writeFile.Write(_fileHeaderInfo.Version);

            // general purpose bit flag
            _writeFile.Write(_fileHeaderInfo.BitFlags);

            // compression method deflate
            _writeFile.Write(_fileHeaderInfo.CompMethod);

            // last mod file time
            _writeFile.Write(_fileHeaderInfo.FileTime);

            // last mod file date
            _writeFile.Write(_fileHeaderInfo.FileDate);

            // CRC-32 is set to zero until CRC will be calculated
            _writeFile.Write(_fileHeaderInfo.FileCRC32);

            // compressed size is set to zero until the compressed size is known
            _writeFile.Write(_fileHeaderInfo.CompSize);

            // uncompressed size
            _writeFile.Write(_fileHeaderInfo.FileSize);

            // convert filename string to array of bytes using DOS (IBM OEM code page 437) encoding
            byte[] zipFileName = Utils.EncodeFilename(_fileHeaderInfo.FileName);

            // file name length
            _writeFile.Write((Int16)zipFileName.Length);

            // extra field length (for file times)
            _writeFile.Write((Int16)(dirFlag ? 0 : 36));

            // file name
            _writeFile.Write(zipFileName);

            // extra field
            if (!dirFlag)
            {
                // NTFS tag of length 32, reserved area of zero, File times tag of 1 with length 24
                _writeFile.Write((UInt32)0x0020000a);
                _writeFile.Write((UInt32)0);
                _writeFile.Write((UInt32)0x00180001);

                var details = fi.GetFileTimeDetails();

                _writeFile.Write(details.LastWriteTimeAsFileTime);
                _writeFile.Write(details.LastAccessTimeAsFileTime);
                _writeFile.Write(details.CreationTimeAsFileTime);
            }

            // save write file length for possible rewind
            _writeRewindPosition = _writeStream.Length;
        }
示例#5
0
        /// <summary>
        /// Decompress ZIP file
        /// </summary>
        /// <param name="fileHeader">The fh.</param>
        /// <param name="rootPathName">Name of the root path, save the decompressed file in this directory.</param>
        /// <param name="newFileName">New name of the file, if this file name is given it overwrite the name in the FH structure</param>
        /// <param name="createPath">if set to <c>true</c> [create path].</param>
        /// <param name="overWrite">if set to <c>true</c> [over write].</param>
        /// <exception cref="UZipDotNet.Exception">
        /// Extract file failed. Invalid directory path
        /// or
        /// Unsupported compression method
        /// or
        /// ZIP file CRC test failed
        /// </exception>
        public void Decompress(FileHeader fileHeader, string rootPathName, string newFileName, bool createPath, bool overWrite)
        {
            try
            {
                // save file header
                _fileHeaderInfo = fileHeader;

                // read file header for this file and compare it to the directory information
                ReadFileHeader(_fileHeaderInfo.FilePos);

                // compressed length
                _readRemain = _fileHeaderInfo.CompSize;
                ReadTotal   = _readRemain;

                // build write file name
                // Root name (optional) plus either original name or a new name
                _writeFileName = (string.IsNullOrEmpty(rootPathName) ? string.Empty :
                                  (rootPathName.EndsWith(Path.DirectorySeparatorChar) ? rootPathName : rootPathName + Path.DirectorySeparatorChar)) +
                                 (string.IsNullOrEmpty(newFileName) ? fileHeader.FileName : newFileName);

                // test if write file name has a path component
                int ptr = _writeFileName.LastIndexOf(Path.DirectorySeparatorChar);
                if (ptr >= 0)
                {
                    // make sure directory exists
                    if (!Directory.Exists(_writeFileName.Substring(0, ptr)))
                    {
                        // make a new folder
                        if (createPath)
                        {
                            Directory.CreateDirectory(_writeFileName.Substring(0, ptr));
                        }

                        // error
                        else
                        {
                            throw new Exception("Extract file failed. Invalid directory path");
                        }
                    }
                }

                // create destination file
                _writeStream = new FileStream(_writeFileName, overWrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write, FileShare.None);

                // convert stream to binary writer
                _writeFile = new BinaryWriter(_writeStream, Encoding.UTF8);

                // reset crc32 checksum
                _writeCrc32 = 0;

                // switch based on compression method
                switch (_fileHeaderInfo.CompMethod)
                {
                // no compression
                case 0:
                    NoCompression();
                    break;

                // deflate compress method
                case 8:
                    // decompress file
                    Decompress();
                    break;

                // not supported
                default:
                    throw new Exception("Unsupported compression method");
                }

                // Zip file checksum is CRC32
                if (_fileHeaderInfo.FileCRC32 != _writeCrc32)
                {
                    throw new Exception("ZIP file CRC test failed");
                }

                // save file length
                WriteTotal = (uint)_writeStream.Length;

                // close write file
                _writeFile.Dispose();
                _writeFile = null;

                // if file times are available set the file time
                if (_fileTimeAvailable)
                {
                    File.SetCreationTime(_writeFileName, _fileCreateTime);
                    File.SetLastWriteTime(_writeFileName, _fileModifyTime);
                    File.SetLastAccessTime(_writeFileName, _fileAccessTime);
                }
                else
                {
                    // convert dos file date and time to DateTime format
                    var fileDosTime = new DateTime(1980 + ((_fileHeaderInfo.FileDate >> 9) & 0x7f),
                                                   (_fileHeaderInfo.FileDate >> 5) & 0xf, _fileHeaderInfo.FileDate & 0x1f,
                                                   (_fileHeaderInfo.FileTime >> 11) & 0x1f, (_fileHeaderInfo.FileTime >> 5) & 0x3f, 2 * (_fileHeaderInfo.FileTime & 0x1f));
                    File.SetCreationTime(_writeFileName, fileDosTime);
                    File.SetLastWriteTime(_writeFileName, fileDosTime);
                    File.SetLastAccessTime(_writeFileName, fileDosTime);
                }

                // set file attribute attributes
                if (_fileHeaderInfo.FileAttr != 0)
                {
                    File.SetAttributes(_writeFileName, _fileHeaderInfo.FileAttr);
                }
            }
            catch (Exception)
            {
                // close the write file if it is open
                if (_writeFile != null)
                {
                    _writeFile.Dispose();
                    _writeFile = null;
                }

                throw;
            }
        }