コード例 #1
0
        void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile)
        {
            var shaFile = Regex.Replace(inputFile, @"\.diff$", ".shasum");
            var expectedReleaseEntry = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(shaFile, Encoding.UTF8));
            var actualReleaseEntry   = ReleaseEntry.GenerateFromFile(tempTargetFile);

            if (expectedReleaseEntry.Filesize != actualReleaseEntry.Filesize)
            {
                this.Log().Warn("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.Filesize, actualReleaseEntry.Filesize);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }

            if (expectedReleaseEntry.SHA1 != actualReleaseEntry.SHA1)
            {
                this.Log().Warn("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.SHA1, actualReleaseEntry.SHA1);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }
        }
コード例 #2
0
        private void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile)
        {
            ReleaseEntry entry  = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(Regex.Replace(inputFile, @"\.(bs)?diff$", ".shasum"), Encoding.UTF8));
            ReleaseEntry entry2 = ReleaseEntry.GenerateFromFile(tempTargetFile, null);

            if (entry.Filesize != entry2.Filesize)
            {
                this.Log <DeltaPackageBuilder>().Warn <string, long, long>("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath, entry.Filesize, entry2.Filesize);
                ChecksumFailedException exception1 = new ChecksumFailedException();
                exception1.Filename = relativeFilePath;
                throw exception1;
            }
            if (entry.SHA1 != entry2.SHA1)
            {
                this.Log <DeltaPackageBuilder>().Warn <string, string, string>("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath, entry.SHA1, entry2.SHA1);
                ChecksumFailedException exception2 = new ChecksumFailedException();
                exception2.Filename = relativeFilePath;
                throw exception2;
            }
        }
コード例 #3
0
        public ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile, Action <int> progress = null)
        {
            Contract.Requires(deltaPackage != null);
            Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile));
            if (progress == null)
            {
                progress = i => { }
            }
            ;

            string workingPath;
            string deltaPath;

            using (Utility.WithTempDirectory(out deltaPath, localAppDirectory))
                using (Utility.WithTempDirectory(out workingPath, localAppDirectory)) {
                    var fz = new FastZip();
                    fz.ExtractZip(deltaPackage.InputPackageFile, deltaPath, null);
                    // The percentages here are somewhat arbitrary and reflect typical Bloom file sizes.
                    // Probably not appropriate to push upstream.
                    // If you are thinking about improving this using FastZip's Progress event, check with JohnT.
                    // I experimented with it and found that the PercentCompleted which it reports is useless,
                    // toggling between 0 and 100% repeatedly...possibly it applies to a particular file?
                    // I also found that it is VERY expensive to turn on progress reporting while unzipping;
                    // seems to slow the process down to the point of making it take two to three times as long.
                    // This may of course improve in a later version of the library.
                    progress(10);
                    fz.ExtractZip(basePackage.InputPackageFile, workingPath, null);
                    progress(35);

                    var pathsVisited = new List <string>();

                    var deltaPathRelativePaths = new DirectoryInfo(deltaPath).GetAllFilesRecursively()
                                                 .Select(x => x.FullName.Replace(deltaPath + Path.DirectorySeparatorChar, ""))
                                                 .ToArray();

                    // Apply all of the .diff files
                    deltaPathRelativePaths
                    .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase))
                    .Where(x => !x.EndsWith(".shasum", StringComparison.InvariantCultureIgnoreCase))
                    .Where(x => !x.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase) ||
                           !deltaPathRelativePaths.Contains(x.Replace(".diff", ".bsdiff")))
                    .ForEach(file => {
                        pathsVisited.Add(Regex.Replace(file, @"\.(bs)?diff$", "").ToLowerInvariant());
                        applyDiffToFile(deltaPath, file, workingPath);
                    });

                    // Delete all of the files that were in the old package but
                    // not in the new one.
                    new DirectoryInfo(workingPath).GetAllFilesRecursively()
                    .Select(x => x.FullName.Replace(workingPath + Path.DirectorySeparatorChar, "").ToLowerInvariant())
                    .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase) && !pathsVisited.Contains(x))
                    .ForEach(x => {
                        this.Log().Info("{0} was in old package but not in new one, deleting", x);
                        File.Delete(Path.Combine(workingPath, x));
                    });

                    // Update all the files that aren't in 'lib' with the delta
                    // package's versions (i.e. the nuspec file, etc etc).
                    deltaPathRelativePaths
                    .Where(x => !x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase))
                    .ForEach(x => {
                        this.Log().Info("Updating metadata file: {0}", x);
                        File.Copy(Path.Combine(deltaPath, x), Path.Combine(workingPath, x), true);
                    });
                    progress(50);

                    this.Log().Info("Repacking into full package: {0}", outputFile);
                    fz.CreateZip(outputFile, workingPath, true, null);
                    progress(100);
                }

            return(new ReleasePackage(outputFile));
        }

        void createDeltaForSingleFile(FileInfo targetFile, DirectoryInfo workingDirectory, Dictionary <string, string> baseFileListing)
        {
            // NB: There are three cases here that we'll handle:
            //
            // 1. Exists only in new => leave it alone, we'll use it directly.
            // 2. Exists in both old and new => write a dummy file so we know
            //    to keep it.
            // 3. Exists in old but changed in new => create a delta file
            //
            // The fourth case of "Exists only in old => delete it in new"
            // is handled when we apply the delta package
            var relativePath = targetFile.FullName.Replace(workingDirectory.FullName, "");

            if (!baseFileListing.ContainsKey(relativePath))
            {
                this.Log().Info("{0} not found in base package, marking as new", relativePath);
                return;
            }

            var oldData = File.ReadAllBytes(baseFileListing[relativePath]);
            var newData = File.ReadAllBytes(targetFile.FullName);

            if (bytesAreIdentical(oldData, newData))
            {
                this.Log().Info("{0} hasn't changed, writing dummy file", relativePath);

                File.Create(targetFile.FullName + ".diff").Dispose();
                File.Create(targetFile.FullName + ".shasum").Dispose();
                targetFile.Delete();
                return;
            }

            this.Log().Info("Delta patching {0} => {1}", baseFileListing[relativePath], targetFile.FullName);
            var msDelta = new MsDeltaCompression();

            try {
                msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff");
            } catch (Win32Exception) {
                this.Log().Warn("We couldn't create a delta for {0}, attempting to create bsdiff", targetFile.Name);

                var of = default(FileStream);
                try {
                    of = File.Create(targetFile.FullName + ".bsdiff");
                    BinaryPatchUtility.Create(oldData, newData, of);

                    // NB: Create a dummy corrupt .diff file so that older
                    // versions which don't understand bsdiff will fail out
                    // until they get upgraded, instead of seeing the missing
                    // file and just removing it.
                    File.WriteAllText(targetFile.FullName + ".diff", "1");
                } catch (Exception ex) {
                    this.Log().WarnException(String.Format("We really couldn't create a delta for {0}", targetFile.Name), ex);
                    return;
                } finally {
                    if (of != null)
                    {
                        of.Dispose();
                    }
                }
            }

            var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum");

            File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
            targetFile.Delete();
        }

        void applyDiffToFile(string deltaPath, string relativeFilePath, string workingDirectory)
        {
            var inputFile   = Path.Combine(deltaPath, relativeFilePath);
            var finalTarget = Path.Combine(workingDirectory, Regex.Replace(relativeFilePath, @"\.(bs)?diff$", ""));

            var tempTargetFile = default(string);

            Utility.WithTempFile(out tempTargetFile, localAppDirectory);

            try {
                // NB: Zero-length diffs indicate the file hasn't actually changed
                if (new FileInfo(inputFile).Length == 0)
                {
                    this.Log().Info("{0} exists unchanged, skipping", relativeFilePath);
                    return;
                }

                if (relativeFilePath.EndsWith(".bsdiff", StringComparison.InvariantCultureIgnoreCase))
                {
                    using (var of = File.OpenWrite(tempTargetFile))
                        using (var inf = File.OpenRead(finalTarget)) {
                            this.Log().Info("Applying BSDiff to {0}", relativeFilePath);
                            BinaryPatchUtility.Apply(inf, () => File.OpenRead(inputFile), of);
                        }

                    verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile);
                }
                else if (relativeFilePath.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase))
                {
                    this.Log().Info("Applying MSDiff to {0}", relativeFilePath);
                    var msDelta = new MsDeltaCompression();
                    msDelta.ApplyDelta(inputFile, finalTarget, tempTargetFile);

                    verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile);
                }
                else
                {
                    using (var of = File.OpenWrite(tempTargetFile))
                        using (var inf = File.OpenRead(inputFile)) {
                            this.Log().Info("Adding new file: {0}", relativeFilePath);
                            inf.CopyTo(of);
                        }
                }

                if (File.Exists(finalTarget))
                {
                    File.Delete(finalTarget);
                }

                var targetPath = Directory.GetParent(finalTarget);
                if (!targetPath.Exists)
                {
                    targetPath.Create();
                }

                File.Move(tempTargetFile, finalTarget);
            } finally {
                if (File.Exists(tempTargetFile))
                {
                    Utility.DeleteFileHarder(tempTargetFile, true);
                }
            }
        }

        void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile)
        {
            var shaFile = Regex.Replace(inputFile, @"\.(bs)?diff$", ".shasum");
            var expectedReleaseEntry = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(shaFile, Encoding.UTF8));
            var actualReleaseEntry   = ReleaseEntry.GenerateFromFile(tempTargetFile);

            if (expectedReleaseEntry.Filesize != actualReleaseEntry.Filesize)
            {
                this.Log().Warn("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.Filesize, actualReleaseEntry.Filesize);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }

            if (expectedReleaseEntry.SHA1 != actualReleaseEntry.SHA1)
            {
                this.Log().Warn("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.SHA1, actualReleaseEntry.SHA1);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }
        }

        bool bytesAreIdentical(byte[] oldData, byte[] newData)
        {
            if (oldData == null || newData == null)
            {
                return(oldData == newData);
            }
            if (oldData.LongLength != newData.LongLength)
            {
                return(false);
            }

            for (long i = 0; i < newData.LongLength; i++)
            {
                if (oldData[i] != newData[i])
                {
                    return(false);
                }
            }

            return(true);
        }
    }