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");