Imported Upstream version 4.4.2.4

Former-commit-id: 92904c9c5915c37244316e42ba99e7b934ed7ee2
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-07-21 09:40:10 +00:00
parent 589d484eee
commit 0b4a830db1
343 changed files with 9849 additions and 688 deletions

View File

@ -82,10 +82,14 @@ namespace SharpCompress.Archive
{
key = key.Substring(1);
}
// .NET allows duplicate entries when saving and loading Zip files.
// The following lines are disabled from upstream SharpCompress to allow this.
#if ZIP_ALLOW_DUPLICATE_KEYS
if (DoesKeyMatchExisting(key))
{
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
}
#endif
var entry = CreateEntry(key, source, size, modified, closeStream);
newEntries.Add(entry);
RebuildModifiedCollection();
@ -101,7 +105,8 @@ namespace SharpCompress.Archive
{
p = p.Substring(1);
}
return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
if (string.Equals(p, key, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}

View File

@ -32,5 +32,10 @@ namespace SharpCompress.Archive.Zip
{
get { return (Parts.Single() as SeekableZipFilePart).Comment; }
}
public override string ToString()
{
return this.Key;
}
}
}

View File

@ -16,6 +16,13 @@ namespace SharpCompress.Common.Zip
this.filePart = filePart;
lastModifiedTime = Utility.DosDateToDateTime(filePart.Header.LastModifiedDate,
filePart.Header.LastModifiedTime);
if (lastModifiedTime == default(DateTime))
{
// On .NET on Windows, for zip entries that don't have a last write time,
// the return value for ZipArchiveEntry.LastWriteTime is:
// 1/1/1980 12:00:00 AM, Ticks=624511296000000000
lastModifiedTime = new DateTime(624511296000000000);
}
}
}

View File

@ -16,6 +16,7 @@ namespace SharpCompress.IO
{
stream.Position = origin.Value;
}
length = bytesToRead;
BytesLeftToRead = bytesToRead;
}
@ -27,6 +28,8 @@ namespace SharpCompress.IO
}
}
private long length;
private long BytesLeftToRead { get; set; }
public Stream Stream { get; private set; }
@ -53,12 +56,12 @@ namespace SharpCompress.IO
public override long Length
{
get { throw new System.NotImplementedException(); }
get { return length; }
}
public override long Position
{
get { throw new System.NotImplementedException(); }
get { return Length - BytesLeftToRead; }
set { throw new System.NotImplementedException(); }
}

View File

@ -238,6 +238,56 @@ namespace MonoTests.System.IO.Compression
File.Delete ("create.zip");
}
[Test]
public void ZipEnumerateArchiveDefaultLastWriteTime()
{
using (var archive = new ZipArchive(File.Open("test.nupkg", FileMode.Open),
ZipArchiveMode.Read))
{
var entry = archive.GetEntry("_rels/.rels");
Assert.AreEqual(new DateTime(624511296000000000).Ticks, entry.LastWriteTime.Ticks);
Assert.IsNotNull(entry);
}
}
public void ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode mode)
{
File.Copy("test.nupkg", "test2.nupkg", overwrite: true);
using (var archive = new ZipArchive(File.Open("test2.nupkg", FileMode.Open), mode))
{
var entry = archive.GetEntry("_rels/.rels");
using (var stream = entry.Open())
{
Assert.AreEqual(0, stream.Position);
Assert.AreEqual(425, stream.Length);
}
// .NET does not support these in Read mode but we do.
var entry2 = archive.GetEntry("modernhttpclient.nuspec");
using (var stream = entry2.Open())
{
Assert.AreEqual(857, stream.Length);
if (mode == ZipArchiveMode.Update)
{
Assert.AreEqual(0, stream.Position);
}
}
}
File.Delete ("test2.nupkg");
}
[Test]
public void ZipGetArchiveEntryStreamLengthPositionReadMode()
{
ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode.Read);
}
[Test]
public void ZipGetArchiveEntryStreamLengthPositionUpdateMode()
{
ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode.Update);
}
[Test]
public void ZipEnumerateEntriesReadMode()
{
@ -258,6 +308,104 @@ namespace MonoTests.System.IO.Compression
File.Delete ("test.zip");
}
[Test]
public void ZipWriteEntriesUpdateMode()
{
File.Copy("archive.zip", "test.zip", overwrite: true);
using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
ZipArchiveMode.Update))
{
var foo = archive.GetEntry("foo.txt");
using (var stream = foo.Open())
using (var sw = new StreamWriter(stream))
{
sw.Write("TEST");
}
}
using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
ZipArchiveMode.Read))
{
var foo = archive.GetEntry("foo.txt");
using (var stream = foo.Open())
using (var sr = new StreamReader(stream))
{
var line = sr.ReadLine();
Assert.AreEqual("TEST", line);
}
}
File.Delete ("test.zip");
}
[Test]
public void ZipWriteEntriesUpdateModeNewEntry()
{
var stream = new MemoryStream();
var zipArchive = new ZipArchive(stream, ZipArchiveMode.Update);
var newEntry = zipArchive.CreateEntry("testEntry");
using (var newStream = newEntry.Open())
{
using (var sw = new StreamWriter(newStream))
{
sw.Write("TEST");
}
}
}
[Test]
public void ZipCreateDuplicateEntriesUpdateMode()
{
var stream = new MemoryStream();
using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Update, true))
{
var e2 = zipArchive.CreateEntry("BBB");
var e3 = zipArchive.CreateEntry("BBB");
}
stream.Position = 0;
using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read))
{
Assert.AreEqual(2, zipArchive.Entries.Count);
}
}
[Test]
public void ZipWriteEntriesUpdateModeNonZeroPosition()
{
File.Copy("archive.zip", "test.zip", overwrite: true);
using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
ZipArchiveMode.Update))
{
var foo = archive.GetEntry("foo.txt");
using (var stream = foo.Open())
{
var line = stream.ReadByte();
using (var sw = new StreamWriter(stream))
{
sw.Write("TEST");
}
}
}
using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
ZipArchiveMode.Read))
{
var entries = archive.Entries;
var foo = archive.GetEntry("foo.txt");
using (var stream = foo.Open())
using (var sr = new StreamReader(stream))
{
var line = sr.ReadLine();
Assert.AreEqual("fTEST", line);
}
}
File.Delete ("test.zip");
}
[Test]
public void ZipEnumerateEntriesUpdateMode()
{
@ -307,5 +455,24 @@ namespace MonoTests.System.IO.Compression
}
File.Delete ("empty.zip");
}
class MyFakeStream : FileStream
{
public MyFakeStream (string path, FileMode mode) : base(path, mode) {}
/// <summary>
/// Simulate "CanSeek" is false, which is the case when you are retreiving data from web.
/// </summary>
public override bool CanSeek => false;
}
[Test]
public void ZipReadNonSeekableStream()
{
var stream = new MyFakeStream("test.nupkg", FileMode.Open);
using (var archive = new ZipArchive (stream, ZipArchiveMode.Read))
{
}
}
}
}

View File

@ -39,7 +39,7 @@ namespace System.IO.Compression
internal readonly ZipArchiveMode mode;
internal Encoding entryNameEncoding;
internal bool disposed;
internal Dictionary<string, ZipArchiveEntry> entries;
internal List<ZipArchiveEntry> entries;
internal SharpCompress.Archive.Zip.ZipArchive zipFile;
public ZipArchive (Stream stream)
@ -49,7 +49,7 @@ namespace System.IO.Compression
this.stream = stream;
mode = ZipArchiveMode.Read;
CreateZip(stream, mode);
CreateZip(mode);
}
public ZipArchive (Stream stream, ZipArchiveMode mode)
@ -59,7 +59,7 @@ namespace System.IO.Compression
this.stream = stream;
this.mode = mode;
CreateZip(stream, mode);
CreateZip(mode);
}
public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen)
@ -70,7 +70,7 @@ namespace System.IO.Compression
this.stream = stream;
this.mode = mode;
leaveStreamOpen = leaveOpen;
CreateZip(stream, mode);
CreateZip(mode);
}
public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding entryNameEncoding)
@ -82,40 +82,59 @@ namespace System.IO.Compression
this.mode = mode;
leaveStreamOpen = leaveOpen;
this.entryNameEncoding = entryNameEncoding;
CreateZip(stream, mode);
CreateZip(mode);
}
private void CreateZip(Stream stream, ZipArchiveMode mode)
private void CreateZip(ZipArchiveMode mode)
{
if (mode != ZipArchiveMode.Read && mode != ZipArchiveMode.Create && mode != ZipArchiveMode.Update)
throw new ArgumentOutOfRangeException("mode");
// If the mode parameter is set to Read, the stream must support reading.
if (mode == ZipArchiveMode.Read && !stream.CanRead)
throw new ArgumentException("Stream must support reading for Read archive mode");
// If the mode parameter is set to Create, the stream must support writing.
if (mode == ZipArchiveMode.Create && !stream.CanWrite)
throw new ArgumentException("Stream must support writing for Create archive mode");
// If the mode parameter is set to Update, the stream must support reading, writing, and seeking.
if (mode == ZipArchiveMode.Update && (!stream.CanRead || !stream.CanWrite || !stream.CanSeek))
throw new ArgumentException("Stream must support reading, writing and seeking for Update archive mode");
try {
zipFile = mode != ZipArchiveMode.Create && stream.Length != 0
? SharpCompress.Archive.Zip.ZipArchive.Open(stream)
: SharpCompress.Archive.Zip.ZipArchive.Create();
} catch (Exception e) {
throw new InvalidDataException("The contents of the stream are not in the zip archive format.", e);
}
if (mode != ZipArchiveMode.Read && mode != ZipArchiveMode.Create && mode != ZipArchiveMode.Update)
throw new ArgumentOutOfRangeException("mode");
entries = new Dictionary<string, ZipArchiveEntry>();
if (Mode != ZipArchiveMode.Create) {
foreach (var entry in zipFile.Entries) {
var zipEntry = new ZipArchiveEntry(this, entry);
entries[entry.Key] = zipEntry;
// If the mode parameter is set to Read, the stream must support reading.
if (mode == ZipArchiveMode.Read && !stream.CanRead)
throw new ArgumentException("Stream must support reading for Read archive mode");
// If the mode parameter is set to Create, the stream must support writing.
if (mode == ZipArchiveMode.Create && !stream.CanWrite)
throw new ArgumentException("Stream must support writing for Create archive mode");
// If the mode parameter is set to Update, the stream must support reading, writing, and seeking.
if (mode == ZipArchiveMode.Update && (!stream.CanRead || !stream.CanWrite || !stream.CanSeek))
throw new ArgumentException("Stream must support reading, writing and seeking for Update archive mode");
// If the stream is not seekable, then buffer it into memory (same behavior as .NET).
if (mode == ZipArchiveMode.Read && !stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
if (!leaveStreamOpen)
stream.Dispose();
this.stream = memoryStream;
}
try {
zipFile = mode != ZipArchiveMode.Create && stream.Length != 0
? SharpCompress.Archive.Zip.ZipArchive.Open(stream)
: SharpCompress.Archive.Zip.ZipArchive.Create();
} catch (Exception e) {
throw new InvalidDataException("The contents of the stream are not in the zip archive format.", e);
}
entries = new List<ZipArchiveEntry>();
if (Mode != ZipArchiveMode.Create) {
foreach (var entry in zipFile.Entries) {
var zipEntry = new ZipArchiveEntry(this, entry);
entries.Add(zipEntry);
}
}
}
catch {
if (!leaveStreamOpen)
stream.Dispose();
throw;
}
}
@ -133,7 +152,7 @@ namespace System.IO.Compression
if (entries == null)
return new ReadOnlyCollection<ZipArchiveEntry>(new List<ZipArchiveEntry>());
return new ReadOnlyCollection<ZipArchiveEntry>(entries.Values.ToList());
return new ReadOnlyCollection<ZipArchiveEntry>(entries);
}
}
@ -154,6 +173,14 @@ namespace System.IO.Compression
return CreateEntry(entryName, CompressionLevel.Optimal);
}
internal SharpCompress.Archive.Zip.ZipArchiveEntry CreateEntryInternal(string entryName)
{
var memoryStream = new MemoryStream();
var entry = zipFile.AddEntry(entryName, memoryStream);
return entry;
}
public ZipArchiveEntry CreateEntry (string entryName, CompressionLevel compressionLevel)
{
if (disposed)
@ -171,10 +198,9 @@ namespace System.IO.Compression
if (zipFile == null)
throw new InvalidDataException("The zip archive is corrupt, and its entries cannot be retrieved.");
var memoryStream = new MemoryStream();
var entry = zipFile.AddEntry(entryName, memoryStream);
var archiveEntry = new ZipArchiveEntry(this, entry);
entries[entryName] = archiveEntry;
var internalEntry = CreateEntryInternal(entryName);
var archiveEntry = new ZipArchiveEntry(this, internalEntry);
entries.Add(archiveEntry);
return archiveEntry;
}
@ -196,7 +222,7 @@ namespace System.IO.Compression
if (zipFile == null)
throw new InvalidDataException("The zip archive is corrupt, and its entries cannot be retrieved.");
return entries.ContainsKey(entryName) ? entries[entryName] : null;
return entries.FirstOrDefault(e => e.FullName == entryName);
}
private void Save()

View File

@ -32,7 +32,7 @@ namespace System.IO.Compression
internal class ZipArchiveEntryStream : Stream, IDisposable
{
private readonly ZipArchiveEntry entry;
private readonly Stream stream;
private Stream stream;
public override bool CanRead {
get {
@ -42,19 +42,19 @@ namespace System.IO.Compression
public override bool CanSeek {
get {
return stream.CanSeek;
return entry.Archive.Mode != ZipArchiveMode.Read;
}
}
public override bool CanWrite {
get {
return stream.CanWrite;
return entry.Archive.Mode != ZipArchiveMode.Read;
}
}
public override long Length {
get {
return stream.Length;
return stream.CanWrite ? stream.Length : entry.Length;
}
}
@ -98,6 +98,34 @@ namespace System.IO.Compression
stream.Write(buffer, offset, count);
}
internal void EnsureWriteable()
{
if (entry.Archive.Mode == ZipArchiveMode.Update && !stream.CanWrite)
{
// Replace the read-only stream with a writeable memory stream.
SetWriteable();
}
}
internal void SetWriteable()
{
var archive = entry.Archive;
var internalEntry = entry.entry;
var newEntry = archive.CreateEntryInternal(internalEntry.Key);
var newStream = newEntry.OpenEntryStream();
var openStream = stream;
openStream.CopyTo(newStream);
openStream.Dispose();
newStream.Position = 0;
archive.zipFile.RemoveEntry(internalEntry);
entry.entry = newEntry;
stream = newStream;
}
public new void Dispose()
{
Dispose(true);
@ -117,8 +145,9 @@ namespace System.IO.Compression
public class ZipArchiveEntry
{
readonly SharpCompress.Archive.Zip.ZipArchiveEntry entry;
internal SharpCompress.Archive.Zip.ZipArchiveEntry entry;
internal ZipArchiveEntryStream openStream;
internal bool wasWritten;
private bool wasDeleted;
internal ZipArchiveEntry(ZipArchive archive, SharpCompress.Archive.Zip.ZipArchiveEntry entry)
@ -174,7 +203,7 @@ namespace System.IO.Compression
if (Archive.disposed)
throw new ObjectDisposedException("The zip archive for this entry has been disposed.");
if (Archive.Mode != ZipArchiveMode.Update)
if (Archive.Mode != ZipArchiveMode.Update)
throw new NotSupportedException("The zip archive for this entry was opened in a mode other than Update.");
if (openStream != null)
@ -198,9 +227,16 @@ namespace System.IO.Compression
if (Archive.Mode == ZipArchiveMode.Create && openStream != null)
throw new IOException("The archive for this entry was opened with the Create mode, and this entry has already been written to.");
openStream = new ZipArchiveEntryStream(this, entry.OpenEntryStream());
var entryStream = entry.OpenEntryStream();
openStream = new ZipArchiveEntryStream(this, entryStream);
openStream.EnsureWriteable();
return openStream;
}
public override string ToString()
{
return FullName;
}
}
}