[System.IO.Compression] Fixed handling of Zip archives with duplicate entries with...
authorJoão Matos <joao@tritao.eu>
Wed, 29 Jun 2016 10:18:16 +0000 (11:18 +0100)
committerJoão Matos <joao@tritao.eu>
Wed, 29 Jun 2016 10:28:16 +0000 (11:28 +0100)
.NET supports this, from MSDN: "If an entry with the specified path and name already exists in the archive, a second entry is created with the same path and name.".

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=42219.

mcs/class/System.IO.Compression/SharpCompress/Archive/AbstractWritableArchive.cs
mcs/class/System.IO.Compression/Test/System.IO.Compression/ZipTest.cs
mcs/class/System.IO.Compression/ZipArchive.cs

index 96af8c4131db2de4bf4b2d0d0b949f2ca6af6278..f844e5cb47158fff394b67ffc0c2454855b3f16f 100644 (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;
         }
index 64b102c2d3dd6b5f062b9869e08b341634ee1e69..996921630022b23d946b5349be4a4052a1aa93e9 100644 (file)
@@ -357,6 +357,23 @@ namespace MonoTests.System.IO.Compression
                        }
                }
 
+               [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()
                {
index 4f0c7bb70b9ecb9d68e9176b5ac97f79e969e00f..9dbf04cd339ccdc84c09cf00af7eb7546f28f66b 100644 (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)
@@ -111,11 +111,11 @@ namespace System.IO.Compression
                                        throw new InvalidDataException("The contents of the stream are not in the zip archive format.", e);
                                }
 
-                               entries = new Dictionary<string, ZipArchiveEntry>();
+                               entries = new List<ZipArchiveEntry>();
                                if (Mode != ZipArchiveMode.Create) {
                                        foreach (var entry in zipFile.Entries) {
                                                var zipEntry = new ZipArchiveEntry(this, entry);
-                                               entries[entry.Key] = zipEntry;
+                                               entries.Add(zipEntry);
                                        }
                                }
                        }
@@ -140,7 +140,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);
                        }
                }
 
@@ -188,7 +188,7 @@ namespace System.IO.Compression
 
                        var internalEntry = CreateEntryInternal(entryName);
                        var archiveEntry = new ZipArchiveEntry(this, internalEntry);
-                       entries[entryName] = archiveEntry;
+                       entries.Add(archiveEntry);
 
                        return archiveEntry;
                }
@@ -210,7 +210,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()