Bundler: Check for duplicate file entries. (dotnet/core-setup#6799)
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>
Sun, 16 Jun 2019 00:41:28 +0000 (17:41 -0700)
committerGitHub <noreply@github.com>
Sun, 16 Jun 2019 00:41:28 +0000 (17:41 -0700)
If the input to the bundler contains multiple entries
with the same relative path, we currently generate a bundle
with a larger size with some unusable files.

Therefore, check this condition when generating bundles, and
throw an error.

The extractor also checks this condition when extracting
malformed bundles.

Commit migrated from https://github.com/dotnet/core-setup/commit/f1c72ba8603d2b13484f37f62b5987564a48be12

src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs
src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs
src/installer/test/BundleTests/Helpers/BundleHelper.cs
src/installer/test/BundleTests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs

index 67a478f..fb15489 100644 (file)
@@ -156,7 +156,12 @@ namespace Microsoft.NET.HostModel.Bundle
             }
             catch (InvalidOperationException)
             {
-                throw new ArgumentException("Input must uniquely specify the host binary");
+                throw new ArgumentException("Invalid input specification: Must specify the host binary");
+            }
+
+            if(fileSpecs.GroupBy(file => file.BundleRelativePath).Where(g => g.Count() > 1).Any())
+            {
+                throw new ArgumentException("Invalid input specification: Found multiple entries with the same BundleRelativePath");
             }
 
             string bundlePath = Path.Combine(OutputDir, HostName);
@@ -187,8 +192,7 @@ namespace Microsoft.NET.HostModel.Bundle
                     {
                         FileType type = InferType(fileSpec.BundleRelativePath, file);
                         long startOffset = AddToBundle(bundle, file, type);
-                        FileEntry entry = new FileEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
-                        BundleManifest.Files.Add(entry);
+                        FileEntry entry = BundleManifest.AddEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
                         trace.Log($"Embed: {entry}");
                     }
                 }
index f24837a..907c869 100644 (file)
@@ -71,6 +71,13 @@ namespace Microsoft.NET.HostModel.Bundle
             BundleID = bundleID;
         }
 
+        public FileEntry AddEntry(FileType type, string relativePath, long offset, long size)
+        {
+            FileEntry entry = new FileEntry(type, relativePath, offset, size);
+            Files.Add(entry);
+            return entry;
+        }
+
         public long Write(BinaryWriter writer)
         {
             long startOffset = writer.BaseStream.Position;
@@ -104,7 +111,7 @@ namespace Microsoft.NET.HostModel.Bundle
             string signature = reader.ReadString();
             if (!signature.Equals(Signature))
             {
-                throw new BundleException("Invalid Bundle");
+                throw new BundleException("Extraction failed: Invalid Bundle");
             }
 
             // The manifest header offset resides just behind the signature.
@@ -130,6 +137,11 @@ namespace Microsoft.NET.HostModel.Bundle
                 manifest.Files.Add(FileEntry.Read(reader));
             }
 
+            if (manifest.Files.GroupBy(file => file.RelativePath).Where(g => g.Count() > 1).Any())
+            {
+                throw new BundleException("Extraction failed: Found multiple entries with the same bundle-relative-path");
+            }
+
             return manifest;
         }
     }
index 8139d6e..71d18fe 100644 (file)
@@ -10,11 +10,27 @@ namespace BundleTests.Helpers
     public static class BundleHelper
     {
         public const string DotnetBundleExtractBaseEnvVariable = "DOTNET_BUNDLE_EXTRACT_BASE_DIR";
+
+        public static string GetHostPath(TestProjectFixture fixture)
+        {
+            return Path.Combine(GetPublishPath(fixture), GetHostName(fixture));
+        }
+
+        public static string GetAppPath(TestProjectFixture fixture)
+        {
+            return Path.Combine(GetPublishPath(fixture), GetAppName(fixture));
+        }
+
         public static string GetHostName(TestProjectFixture fixture)
         {
             return Path.GetFileName(fixture.TestProject.AppExe);
         }
 
+        public static string GetAppName(TestProjectFixture fixture)
+        {
+            return Path.GetFileName(fixture.TestProject.AppDll);
+        }
+
         public static string GetPublishPath(TestProjectFixture fixture)
         {
             return Path.Combine(fixture.TestProject.ProjectDirectory, "publish");
index c823f5a..5773777 100644 (file)
@@ -50,7 +50,6 @@ namespace Microsoft.NET.HostModel.Tests
 
             var hostName = BundleHelper.GetHostName(fixture);
             var appName = Path.GetFileNameWithoutExtension(hostName);
-            string publishPath = BundleHelper.GetPublishPath(fixture);
             var bundleDir = BundleHelper.GetBundleDir(fixture);
 
             // Generate a file specification without the apphost
@@ -63,6 +62,24 @@ namespace Microsoft.NET.HostModel.Tests
             Assert.Throws<ArgumentException>(() => bundler.GenerateBundle(fileSpecs));
         }
 
+        [Fact]
+        public void TestWithDuplicateEntriesFails()
+        {
+            var fixture = sharedTestState.TestFixture.Copy();
+
+            var hostName = BundleHelper.GetHostName(fixture);
+            var bundleDir = BundleHelper.GetBundleDir(fixture);
+
+            // Generate a file specification with duplicate entries
+            var fileSpecs = new List<FileSpec>();
+            fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture)));
+            fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "app.repeat"));
+            fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "app.repeat"));
+
+            Bundler bundler = new Bundler(hostName, bundleDir.FullName);
+            Assert.Throws<ArgumentException>(() => bundler.GenerateBundle(fileSpecs));
+        }
+
         [InlineData(true)]
         [InlineData(false)]
         [Theory]