diff --git a/ICSharpCode.SharpZipLib/Tar/TarArchive.cs b/ICSharpCode.SharpZipLib/Tar/TarArchive.cs index 920152c87..02b1e1d98 100644 --- a/ICSharpCode.SharpZipLib/Tar/TarArchive.cs +++ b/ICSharpCode.SharpZipLib/Tar/TarArchive.cs @@ -556,6 +556,9 @@ public void ExtractContents(string destinationDirectory) break; } + if (entry.TarHeader.TypeFlag == TarHeader.LF_LINK || entry.TarHeader.TypeFlag == TarHeader.LF_SYMLINK) + continue; + ExtractEntry(destinationDirectory, entry); } } diff --git a/ICSharpCode.SharpZipLib/Tar/TarHeader.cs b/ICSharpCode.SharpZipLib/Tar/TarHeader.cs index 4a5cb8655..6178e2379 100644 --- a/ICSharpCode.SharpZipLib/Tar/TarHeader.cs +++ b/ICSharpCode.SharpZipLib/Tar/TarHeader.cs @@ -147,6 +147,11 @@ public class TarHeader : ICloneable /// public const int DEVLEN = 8; + /// + /// The length of the name prefix field in a header buffer. + /// + public const int PREFIXLEN = 155; + // // LF_ constants represent the "type" of an entry // @@ -597,21 +602,26 @@ public void ParseBuffer(byte[] header) Magic = ParseName(header, offset, MAGICLEN).ToString(); offset += MAGICLEN; - Version = ParseName(header, offset, VERSIONLEN).ToString(); - offset += VERSIONLEN; + if (Magic == "ustar") + { + Version = ParseName(header, offset, VERSIONLEN).ToString(); + offset += VERSIONLEN; - UserName = ParseName(header, offset, UNAMELEN).ToString(); - offset += UNAMELEN; + UserName = ParseName(header, offset, UNAMELEN).ToString(); + offset += UNAMELEN; - GroupName = ParseName(header, offset, GNAMELEN).ToString(); - offset += GNAMELEN; + GroupName = ParseName(header, offset, GNAMELEN).ToString(); + offset += GNAMELEN; - DevMajor = (int)ParseOctal(header, offset, DEVLEN); - offset += DEVLEN; + DevMajor = (int) ParseOctal(header, offset, DEVLEN); + offset += DEVLEN; - DevMinor = (int)ParseOctal(header, offset, DEVLEN); + DevMinor = (int) ParseOctal(header, offset, DEVLEN); + offset += DEVLEN; - // Fields past this point not currently parsed or used... + string prefix = ParseName(header, offset, PREFIXLEN).ToString(); + if (!string.IsNullOrEmpty(prefix)) Name = prefix + '/' + Name; + } isChecksumValid = Checksum == TarHeader.MakeCheckSum(header); } diff --git a/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs b/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs index 522715eb5..3ee8216b5 100644 --- a/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs +++ b/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs @@ -491,6 +491,8 @@ public TarEntry GetNextEntry() headerBuf = this.tarBuffer.ReadBlock(); } else if (header.TypeFlag != TarHeader.LF_NORMAL && header.TypeFlag != TarHeader.LF_OLDNORM && + header.TypeFlag != TarHeader.LF_LINK && + header.TypeFlag != TarHeader.LF_SYMLINK && header.TypeFlag != TarHeader.LF_DIR) { // Ignore things we dont understand completely for now SkipToNextEntry(); diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs index bdff5e4cf..feb86a39b 100644 --- a/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs +++ b/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs @@ -257,6 +257,7 @@ public ZipEntry(ZipEntry entry) compressedSize = entry.compressedSize; crc = entry.crc; dosTime = entry.dosTime; + dateTime = entry.dateTime; method = entry.method; comment = entry.comment; versionToExtract = entry.versionToExtract; @@ -706,16 +707,7 @@ public long DosTime /// public DateTime DateTime { - get - { - uint sec = Math.Min(59, 2 * (dosTime & 0x1f)); - uint min = Math.Min(59, (dosTime >> 5) & 0x3f); - uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f); - uint mon = Math.Max(1, Math.Min(12, ((dosTime >> 21) & 0xf))); - uint year = ((dosTime >> 25) & 0x7f) + 1980; - int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f))); - return new System.DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec); - } + get { return dateTime; } set { @@ -1041,49 +1033,41 @@ internal void ProcessExtraData(bool localHeader) } } - if (extraData.Find(10)) { - // No room for any tags. - if (extraData.ValueLength < 4) { - throw new ZipException("NTFS Extra data invalid"); - } - - extraData.ReadInt(); // Reserved - - while (extraData.UnreadCount >= 4) { - int ntfsTag = extraData.ReadShort(); - int ntfsLength = extraData.ReadShort(); - if (ntfsTag == 1) { - if (ntfsLength >= 24) { - long lastModification = extraData.ReadLong(); - long lastAccess = extraData.ReadLong(); - long createTime = extraData.ReadLong(); - - DateTime = System.DateTime.FromFileTime(lastModification); - } - break; - } else { - // An unknown NTFS tag so simply skip it. - extraData.Skip(ntfsLength); - } - } - } else if (extraData.Find(0x5455)) { - int length = extraData.ValueLength; - int flags = extraData.ReadByte(); - - // Can include other times but these are ignored. Length of data should - // actually be 1 + 4 * no of bits in flags. - if (((flags & 1) != 0) && (length >= 5)) { - int iTime = extraData.ReadInt(); - - DateTime = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + - new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); - } - } + dateTime = GetDateTime(extraData); if (method == CompressionMethod.WinZipAES) { ProcessAESExtraData(extraData); } } + private DateTime GetDateTime(ZipExtraData extraData) { + // Check for NT timestamp + // NOTE: Disable by default to match behavior of InfoZIP +#if RESPECT_NT_TIMESTAMP + NTTaggedData ntData = extraData.GetData(); + if (ntData != null) + return ntData.LastModificationTime; +#endif + + // Check for Unix timestamp + ExtendedUnixData unixData = extraData.GetData(); + if (unixData != null && + // Only apply modification time, but require all other values to be present + // This is done to match InfoZIP's behaviour + ((unixData.Include & ExtendedUnixData.Flags.ModificationTime) != 0) && + ((unixData.Include & ExtendedUnixData.Flags.AccessTime) != 0) && + ((unixData.Include & ExtendedUnixData.Flags.CreateTime) != 0)) + return unixData.ModificationTime; + + // Fall back to DOS time + uint sec = Math.Min(59, 2 * (dosTime & 0x1f)); + uint min = Math.Min(59, (dosTime >> 5) & 0x3f); + uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f); + uint mon = Math.Max(1, Math.Min(12, ((dosTime >> 21) & 0xf))); + uint year = ((dosTime >> 25) & 0x7f) + 1980; + int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f))); + return new DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec, DateTimeKind.Utc); + } + // For AES the method in the entry is 99, and the real compression method is in the extradata // private void ProcessAESExtraData(ZipExtraData extraData) @@ -1281,6 +1265,7 @@ public static string CleanName(string name) ushort versionToExtract; // Version required to extract (library handles <= 2.0) uint crc; uint dosTime; + DateTime dateTime; CompressionMethod method = CompressionMethod.Deflated; byte[] extra; diff --git a/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs b/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs index e0aae6c58..63cde0bdb 100644 --- a/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs +++ b/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs @@ -190,25 +190,29 @@ public void SetData(byte[] data, int index, int count) // bit 2 if set, creation time is present _flags = (Flags)helperStream.ReadByte(); - if (((_flags & Flags.ModificationTime) != 0) && (count >= 5)) { + if (((_flags & Flags.ModificationTime) != 0)) + { int iTime = helperStream.ReadLEInt(); - _modificationTime = (new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + - new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); + _modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); + + // Central-header version is truncated after modification time + if (count <= 5) return; } if ((_flags & Flags.AccessTime) != 0) { int iTime = helperStream.ReadLEInt(); - _lastAccessTime = (new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + - new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); + _lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); } if ((_flags & Flags.CreateTime) != 0) { int iTime = helperStream.ReadLEInt(); - _createTime = (new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + - new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); + _createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); } } } @@ -224,17 +228,17 @@ public byte[] GetData() helperStream.IsStreamOwner = false; helperStream.WriteByte((byte)_flags); // Flags if ((_flags & Flags.ModificationTime) != 0) { - TimeSpan span = _modificationTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime(); + TimeSpan span = _modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var seconds = (int)span.TotalSeconds; helperStream.WriteLEInt(seconds); } if ((_flags & Flags.AccessTime) != 0) { - TimeSpan span = _lastAccessTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime(); + TimeSpan span = _lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var seconds = (int)span.TotalSeconds; helperStream.WriteLEInt(seconds); } if ((_flags & Flags.CreateTime) != 0) { - TimeSpan span = _createTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime(); + TimeSpan span = _createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var seconds = (int)span.TotalSeconds; helperStream.WriteLEInt(seconds); } @@ -321,7 +325,7 @@ public DateTime CreateTime /// /// Get/set the values to include. /// - Flags Include + public Flags Include { get { return _flags; } set { _flags = value; } @@ -365,13 +369,13 @@ public void SetData(byte[] data, int index, int count) if (ntfsTag == 1) { if (ntfsLength >= 24) { long lastModificationTicks = helperStream.ReadLELong(); - _lastModificationTime = DateTime.FromFileTime(lastModificationTicks); + _lastModificationTime = DateTime.FromFileTimeUtc(lastModificationTicks); long lastAccessTicks = helperStream.ReadLELong(); - _lastAccessTime = DateTime.FromFileTime(lastAccessTicks); + _lastAccessTime = DateTime.FromFileTimeUtc(lastAccessTicks); long createTimeTicks = helperStream.ReadLELong(); - _createTime = DateTime.FromFileTime(createTimeTicks); + _createTime = DateTime.FromFileTimeUtc(createTimeTicks); } break; } else { @@ -394,9 +398,9 @@ public byte[] GetData() helperStream.WriteLEInt(0); // Reserved helperStream.WriteLEShort(1); // Tag helperStream.WriteLEShort(24); // Length = 3 x 8. - helperStream.WriteLELong(_lastModificationTime.ToFileTime()); - helperStream.WriteLELong(_lastAccessTime.ToFileTime()); - helperStream.WriteLELong(_createTime.ToFileTime()); + helperStream.WriteLELong(_lastModificationTime.ToFileTimeUtc()); + helperStream.WriteLELong(_lastAccessTime.ToFileTimeUtc()); + helperStream.WriteLELong(_createTime.ToFileTimeUtc()); return ms.ToArray(); } } @@ -469,9 +473,9 @@ public DateTime LastAccessTime } #region Instance Fields - DateTime _lastAccessTime = DateTime.FromFileTime(0); - DateTime _lastModificationTime = DateTime.FromFileTime(0); - DateTime _createTime = DateTime.FromFileTime(0); + DateTime _lastAccessTime = DateTime.FromFileTimeUtc(0); + DateTime _lastModificationTime = DateTime.FromFileTimeUtc(0); + DateTime _createTime = DateTime.FromFileTimeUtc(0); #endregion } @@ -575,33 +579,18 @@ public Stream GetStreamForTag(int tag) /// /// Get the tagged data for a tag. /// - /// The tag to search for. + /// The tag to search for. /// Returns a tagged value or null if none found. - private ITaggedData GetData(short tag) + public T GetData() + where T : class, ITaggedData, new() { - ITaggedData result = null; - if (Find(tag)) { - result = Create(tag, _data, _readValueStart, _readValueLength); - } - return result; - } - - static ITaggedData Create(short tag, byte[] data, int offset, int count) - { - ITaggedData result = null; - switch (tag) { - case 0x000A: - result = new NTTaggedData(); - break; - case 0x5455: - result = new ExtendedUnixData(); - break; - default: - result = new RawTaggedData(tag); - break; + T result = new T(); + if (Find(result.TagID)) + { + result.SetData(_data, _readValueStart, _readValueLength); + return result; } - result.SetData(data, offset, count); - return result; + else return null; } /// diff --git a/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 5dc32b2eb..9c4000088 100644 --- a/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -1194,16 +1194,17 @@ long TestLocalHeader(ZipEntry entry, HeaderTest tests) // Size can be verified only if it is known in the local header. // it will always be known in the central header. if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) || - ((size > 0) || (compressedSize > 0))) { + ((size > 0 || compressedSize > 0) && entry.Size > 0)) { - if (size != entry.Size) { + if ((size != 0) + && (size != entry.Size)) { throw new ZipException( string.Format("Size mismatch between central header({0}) and local header({1})", entry.Size, size)); } - if (compressedSize != entry.CompressedSize && - compressedSize != 0xFFFFFFFF && compressedSize != -1) { + if ((compressedSize != 0) + && (compressedSize != entry.CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != -1)) { throw new ZipException( string.Format("Compressed size mismatch between central header({0}) and local header({1})", entry.CompressedSize, compressedSize)); @@ -3196,7 +3197,7 @@ Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false); byte[] pwdVerifyCalc = decryptor.PwdVerifier; if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1]) - throw new Exception("Invalid password for AES"); + throw new ZipException("Invalid password for AES"); result = new ZipAESStream(baseStream, decryptor, CryptoStreamMode.Read); } else { throw new ZipException("Decryption method not supported");