/// <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(); }
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); }
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")); }
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"); }
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(); }
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(); }
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(); }
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; } } }
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(); } }
/// <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); }
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(); }
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(); }
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(); }
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(); }
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")); }