示例#1
0
        /// <inheritdoc/>
        protected override void HandleFile(FileInfo file, bool executable = false)
        {
            #region Sanity checks
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            #endregion

            var entry = new TarEntry(new TarHeader
            {
                Name    = file.RelativeTo(SourceDirectory),
                ModTime = file.LastWriteTimeUtc,
                Mode    = (executable ? TarExtractor.DefaultMode | TarExtractor.ExecuteMode : TarExtractor.DefaultMode)
            });

            var hardlinkTarget = _previousFiles.FirstOrDefault(previousFile => FileUtils.AreHardlinked(previousFile.FullName, file.FullName));
            if (hardlinkTarget != null)
            {
                entry.TarHeader.TypeFlag = TarHeader.LF_LINK;
                entry.TarHeader.LinkName = hardlinkTarget.RelativeTo(SourceDirectory);
                _tarStream.PutNextEntry(entry);
            }
            else
            {
                _previousFiles.Add(file);

                entry.Size = file.Length;
                _tarStream.PutNextEntry(entry);
                using (var stream = file.OpenRead())
                    stream.CopyToEx(_tarStream);
            }
            _tarStream.CloseEntry();
        }
示例#2
0
        private bool JoinWithHardlink(StoreFile file1, StoreFile file2)
        {
            if (FileUtils.AreHardlinked(file1, file2))
            {
                return(false);
            }

            if (_unsealedImplementations.Add(file1.ImplementationPath))
            {
                FileUtils.DisableWriteProtection(file1.ImplementationPath);
            }
            if (_unsealedImplementations.Add(file2.ImplementationPath))
            {
                FileUtils.DisableWriteProtection(file2.ImplementationPath);
            }

            string tempFile = Path.Combine(_storePath, Path.GetRandomFileName());

            try
            {
                Log.Info("Hard link: " + file1 + " <=> " + file2);
                FileUtils.CreateHardlink(tempFile, file2);
                FileUtils.Replace(tempFile, file1);
            }
            finally
            {
                if (File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
            }
            return(true);
        }
示例#3
0
        public void Hardlink()
        {
            var root = new TestRoot
            {
                new TestDirectory("dir")
                {
                    new TestFile("file"),
                    new TestFile("executable")
                    {
                        IsExecutable = true
                    },
                    new TestSymlink("symlink", "target")
                }
            };

            root.Build(SourceDirectory);

            FileUtils.EnableWriteProtection(SourceDirectory); // Hard linking logic should work around write-protection by temporarily removing it
            try
            {
                new CloneDirectory(SourceDirectory, TargetDirectory)
                {
                    UseHardlinks = true
                }.Run();
            }
            finally
            {
                FileUtils.DisableWriteProtection(SourceDirectory);
            }

            root.Verify(TargetDirectory);
            FileUtils.AreHardlinked(Path.Combine(SourceDirectory, "dir", "file"), Path.Combine(TargetDirectory, "dir", "file"));
            FileUtils.AreHardlinked(Path.Combine(SourceDirectory, "dir", "executable"), Path.Combine(TargetDirectory, "dir", "executable"));
        }
示例#4
0
        public void TestExtractUnixArchiveWithHardlink()
        {
            using (var extractor = new TarExtractor(typeof(TarExtractorTest).GetEmbeddedStream("testArchive.tar"), _sandbox))
                extractor.Run();

            FileUtils.AreHardlinked(Path.Combine(_sandbox, "subdir1", "regular"), Path.Combine(_sandbox, "hardlink"))
            .Should().BeTrue(because: "'regular' and 'hardlink' should be hardlinked together");
        }
    public void Hardlink()
    {
        using var tempDir = new TemporaryDirectory("0install-test-archive");
        var builder = new DirectoryBuilder(tempDir);
        using var stream = typeof(ArchiveExtractorTestBase).GetEmbeddedStream(FileName);
        ArchiveExtractor.For(MimeType, new SilentTaskHandler())
                        .Extract(builder, stream);

        FileUtils.AreHardlinked(Path.Combine(tempDir, "hardlink"), Path.Combine(tempDir, "subdir1", "regular"))
                 .Should().BeTrue(because: "'regular' and 'hardlink' should be hardlinked together");
    }
示例#6
0
        public void ShouldNotHardlinkWithDifferentTimestamps()
        {
            string package1Path = DeployPackage("sha256=1", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1))
                                                .AddFile("fileX", "abc", new DateTime(2000, 2, 2)));

            _store.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package1Path, "fileX")).Should().BeFalse();
        }
示例#7
0
        public void ShouldNotHardlinkDifferentFiles()
        {
            string package1Path = DeployPackage("sha256=1", new PackageBuilder()
                                                .AddFolder("dir").AddFile("fileB", "abc", new DateTime(2000, 1, 1)));
            string package2Path = DeployPackage("sha256=2", new PackageBuilder()
                                                .AddFolder("dir").AddFile("fileB", "def", new DateTime(2000, 1, 1)));

            _store.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "dir", "fileB"),
                Path.Combine(package2Path, "dir", "fileB")).Should().BeFalse();
        }
示例#8
0
        public void ShouldNotHardlinkAcrossManifestFormatBorders()
        {
            string package1Path = DeployPackage("sha256=1", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1)));
            string package2Path = DeployPackage("sha256new_2", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1)));

            _store.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package2Path, "fileA")).Should().BeFalse();
        }
示例#9
0
        public void ShouldHardlinkIdenticalFilesInSameImplementation()
        {
            string package1Path = DeployPackage("sha256=1", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1))
                                                .AddFolder("dir").AddFile("fileB", "abc", new DateTime(2000, 1, 1)));

            _store.Optimise(_handler).Should().Be(3);
            _store.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package1Path, "dir", "fileB")).Should().BeTrue();
        }
        public void ShouldHardlinkIdenticalFilesInDifferentImplementations()
        {
            string package1Path = DeployPackage("sha256=1", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1)));
            string package2Path = DeployPackage("sha256=2", new PackageBuilder()
                                                .AddFile("fileA", "abc", new DateTime(2000, 1, 1)));

            Assert.AreEqual(3, _store.Optimise(_handler));
            Assert.AreEqual(0, _store.Optimise(_handler));
            Assert.IsTrue(FileUtils.AreHardlinked(
                              Path.Combine(package1Path, "fileA"),
                              Path.Combine(package2Path, "fileA")));
        }
            /// <summary>
            /// Executes the work-step for a single implementation.
            /// </summary>
            public void Work(ManifestDigest manifestDigest)
            {
                string?digestString = manifestDigest.Best;

                if (digestString == null)
                {
                    return;
                }
                string implementationPath = Path.Combine(_storePath, digestString);
                var    manifest           = Manifest.Load(Path.Combine(implementationPath, Manifest.ManifestFile), ManifestFormat.FromPrefix(digestString));

                string currentDirectory = "";

                foreach (var node in manifest)
                {
                    switch (node)
                    {
                    case ManifestDirectory x:
                        currentDirectory = FileUtils.UnifySlashes(x.FullPath.TrimStart('/'));
                        break;

                    case ManifestFileBase x:
                        if (x.Size == 0)
                        {
                            return;
                        }

                        var key  = new DedupKey(x.Size, x.ModifiedTime, manifest.Format, x.Digest);
                        var file = new StoreFile(implementationPath, Path.Combine(currentDirectory, x.Name));

                        if (_fileHashes.TryGetValue(key, out var existingFile))
                        {
                            if (!FileUtils.AreHardlinked(file, existingFile))
                            {
                                if (JoinWithHardlink(file, existingFile))
                                {
                                    SavedBytes += x.Size;
                                }
                            }
                        }
                        else
                        {
                            _fileHashes.Add(key, file);
                        }
                        break;
                    }
                }
            }
示例#12
0
        public void TestCreateHardlink()
        {
            using (var tempDir = new TemporaryDirectory("unit-tests"))
            {
                string sourcePath      = Path.Combine(tempDir, "hardlink");
                string destinationPath = Path.Combine(tempDir, "target");
                string copyPath        = Path.Combine(tempDir, "copy");

                File.WriteAllText(destinationPath, @"data");
                FileUtils.CreateHardlink(sourcePath, destinationPath);
                File.Exists(sourcePath).Should().BeTrue(because: "Hardlink should look like regular file");
                File.ReadAllText(sourcePath).Should().Be("data", because: "Hardlinked file contents should be equal");
                FileUtils.AreHardlinked(sourcePath, destinationPath).Should().BeTrue();

                File.Copy(sourcePath, copyPath);
                FileUtils.AreHardlinked(sourcePath, copyPath).Should().BeFalse();
            }
        }
示例#13
0
        /// <summary>
        /// Executes the work-step for a single implementation.
        /// </summary>
        public void Work(ManifestDigest manifestDigest)
        {
            string digestString = manifestDigest.Best;

            if (digestString == null)
            {
                return;
            }
            string implementationPath = Path.Combine(_storePath, digestString);
            var    manifest           = Manifest.Load(Path.Combine(implementationPath, Manifest.ManifestFile), ManifestFormat.FromPrefix(digestString));

            string currentDirectory = "";

            new AggregateDispatcher <ManifestNode>
            {
                (ManifestDirectory x) => { currentDirectory = FileUtils.UnifySlashes(x.FullPath.TrimStart('/')); },
                (ManifestFileBase x) =>
                {
                    if (x.Size == 0)
                    {
                        return;
                    }

                    var key  = new DedupKey(x.Size, x.ModifiedTime, manifest.Format, x.Digest);
                    var file = new StoreFile(implementationPath, Path.Combine(currentDirectory, x.Name));

                    StoreFile existingFile;
                    if (_fileHashes.TryGetValue(key, out existingFile))
                    {
                        if (!FileUtils.AreHardlinked(file, existingFile))
                        {
                            if (JoinWithHardlink(file, existingFile))
                            {
                                SavedBytes += x.Size;
                            }
                        }
                    }
                    else
                    {
                        _fileHashes.Add(key, file);
                    }
                }
            }.Dispatch(manifest);
        }
示例#14
0
        public void ShouldNotHardlinkFilesWithDifferentTimestamps()
        {
            string package1Path = DeployPackage("sha256=1", new TestRoot
            {
                new TestFile("fileA")
                {
                    Contents = "abc", LastWrite = new DateTime(2000, 1, 1)
                },
                new TestFile("fileX")
                {
                    Contents = "abc", LastWrite = new DateTime(2000, 2, 2)
                }
            });

            _implementationStore.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package1Path, "fileX")).Should().BeFalse();
        }
示例#15
0
        public void ShouldNotHardlinkAcrossManifestFormatBorders()
        {
            string package1Path = DeployPackage("sha256=1", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "abc"
                }
            });
            string package2Path = DeployPackage("sha256new_2", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "abc"
                }
            });

            _implementationStore.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package2Path, "fileA")).Should().BeFalse();
        }
示例#16
0
        public void ShouldNotHardlinkFilesWithDifferentContent()
        {
            string package1Path = DeployPackage("sha256=1", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "abc"
                }
            });
            string package2Path = DeployPackage("sha256=2", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "def"
                }
            });

            _implementationStore.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package2Path, "fileA")).Should().BeFalse();
        }
示例#17
0
        public void ShouldHardlinkIdenticalFilesInDifferentImplementations()
        {
            string package1Path = DeployPackage("sha256=1", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "abc"
                }
            });
            string package2Path = DeployPackage("sha256=2", new TestRoot {
                new TestFile("fileA")
                {
                    Contents = "abc"
                }
            });

            _implementationStore.Optimise(_handler).Should().Be(3);
            _implementationStore.Optimise(_handler).Should().Be(0);
            FileUtils.AreHardlinked(
                Path.Combine(package1Path, "fileA"),
                Path.Combine(package2Path, "fileA")).Should().BeTrue();
        }
示例#18
0
        public void HardlinkFile()
        {
            new TestRoot {
                new TestFile("fileA")
            }.Build(SourceDirectory);

            FileUtils.EnableWriteProtection(SourceDirectory); // Hard linking logic should work around write-protection by temporarily removing it
            try
            {
                new CloneFile(Path.Combine(SourceDirectory, "fileA"), TargetDirectory)
                {
                    TargetFileName = "fileB", UseHardlinks = true
                }.Run();
            }
            finally
            {
                FileUtils.DisableWriteProtection(SourceDirectory);
            }

            new TestRoot {
                new TestFile("fileB")
            }.Verify(TargetDirectory);
            FileUtils.AreHardlinked(Path.Combine(SourceDirectory, "fileA"), Path.Combine(TargetDirectory, "fileB"));
        }