diff --git a/internal/deb/extract.go b/internal/deb/extract.go index e502a952..91065860 100644 --- a/internal/deb/extract.go +++ b/internal/deb/extract.go @@ -320,7 +320,7 @@ func extractData(pkgReader io.ReadSeeker, options *ExtractOptions) error { pendingPaths: pendingPaths, } - err = handlePendingHardLinks(extractHardLinkOptions) + err = extractHardLinks(extractHardLinkOptions) if err != nil { return err } @@ -349,7 +349,7 @@ type extractHardLinkOptions struct { pendingPaths map[string]bool } -func handlePendingHardLinks(opts *extractHardLinkOptions) error { +func extractHardLinks(opts *extractHardLinkOptions) error { for { tarHeader, err := opts.tarReader.Next() if err == io.EOF { diff --git a/internal/deb/extract_test.go b/internal/deb/extract_test.go index 1f0828b0..0cc6c349 100644 --- a/internal/deb/extract_test.go +++ b/internal/deb/extract_test.go @@ -355,7 +355,7 @@ var extractTests = []extractTest{{ }, error: `cannot extract from package "test-package": path /dir/ requested twice with diverging mode: 0777 != 0000`, }, { - summary: "Hard link is inflated with the target file", + summary: "Hard link is linked to the target file", pkgdata: testutil.MustMakeDeb([]testutil.TarEntry{ testutil.Dir(0755, "./"), testutil.Reg(0644, "./file", "text for file"), @@ -363,15 +363,39 @@ var extractTests = []extractTest{{ }), options: deb.ExtractOptions{ Extract: map[string][]deb.ExtractInfo{ + "/file": []deb.ExtractInfo{{ + Path: "/file", + }}, "/hardlink": []deb.ExtractInfo{{ Path: "/hardlink", }}, }, }, result: map[string]string{ + "/file": "file 0644 28121945", "/hardlink": "file 0644 28121945", }, notCreated: []string{}, +}, { + summary: "Hard link is inflated with the target file", + pkgdata: testutil.MustMakeDeb([]testutil.TarEntry{ + testutil.Dir(0755, "./"), + testutil.Reg(0644, "./file", "text for file"), + testutil.Hln(0644, "./hardlink1", "./file"), + testutil.Hln(0644, "./hardlink2", "./file"), + }), + options: deb.ExtractOptions{ + Extract: map[string][]deb.ExtractInfo{ + "/hardlink*": []deb.ExtractInfo{{ + Path: "/hardlink*", + }}, + }, + }, + result: map[string]string{ + "/hardlink1": "file 0644 28121945", + "/hardlink2": "file 0644 28121945", + }, + notCreated: []string{}, }, { summary: "Dangling hard link", pkgdata: testutil.MustMakeDeb([]testutil.TarEntry{ @@ -405,29 +429,6 @@ var extractTests = []extractTest{{ "/symlink": "symlink ./file", }, notCreated: []string{}, -}, { - summary: "Extract all types of files", - pkgdata: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Dir(0755, "./dir/"), - testutil.Reg(0644, "./dir/file", "text for file"), - testutil.Lnk(0644, "./symlink", "./dir/file"), - testutil.Hln(0644, "./hardlink", "./dir/file"), - }), - options: deb.ExtractOptions{ - Extract: map[string][]deb.ExtractInfo{ - "/**": []deb.ExtractInfo{{ - Path: "/**", - }}, - }, - }, - result: map[string]string{ - "/dir/": "dir 0755", - "/dir/file": "file 0644 28121945", - "/hardlink": "file 0644 28121945", - "/symlink": "symlink ./dir/file", - }, - notCreated: []string{}, }, { summary: "Explicit extraction overrides existing file", pkgdata: testutil.PackageData["test-package"], diff --git a/internal/fsutil/create_test.go b/internal/fsutil/create_test.go index 6db27bd0..c4f22e8f 100644 --- a/internal/fsutil/create_test.go +++ b/internal/fsutil/create_test.go @@ -282,7 +282,8 @@ func (s *S) TestCreate(c *C) { // [fsutil.Create] does not return information about parent directories // created implicitly. We only check for the requested path. if entry.Link != "" && entry.Mode&fs.ModeSymlink == 0 { - // Entry is a hard link. + // Entry is a hard link. We should test it differently to ensure + // that it produces a hard link indeed. pathInfo, err := os.Lstat(entry.Path) c.Assert(err, IsNil) linkInfo, err := os.Lstat(entry.Link) diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index 01b2ec11..6b4f8011 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -1653,7 +1653,8 @@ var slicerTests = []slicerTest{{ "/hardlink2.txt": "file 0644 dcddda2e <2> {test-package_myslice}", }, }, { - summary: "Symlink is a valid hard link base file", slices: []setup.SliceKey{ + summary: "Symlink is a valid hard link base file", + slices: []setup.SliceKey{ {"test-package", "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", @@ -1683,6 +1684,60 @@ var slicerTests = []slicerTest{{ "/symlink": "symlink ./dir/file <1> {test-package_myslice}", "/hardlink": "symlink ./dir/file <1> {test-package_myslice}", }, +}, { + summary: "Hard link IDs are unique to multiple packages", + slices: []setup.SliceKey{ + {"test-package1", "myslice"}, + {"test-package2", "myslice"}, + }, + pkgs: []*testutil.TestPackage{{ + Name: "test-package1", + Data: testutil.MustMakeDeb([]testutil.TarEntry{ + testutil.Dir(0755, "./"), + testutil.Dir(0755, "./dir/"), + testutil.Reg(0644, "./dir/file1", "text for file"), + testutil.Hln(0644, "./hardlink1", "./dir/file1"), + }), + }, { + Name: "test-package2", + Data: testutil.MustMakeDeb([]testutil.TarEntry{ + testutil.Dir(0755, "./"), + testutil.Dir(0755, "./dir/"), + testutil.Reg(0644, "./dir/file2", "text for file"), + testutil.Hln(0644, "./hardlink2", "./dir/file2"), + }), + }}, + release: map[string]string{ + "slices/mydir/test-package1.yaml": ` + package: test-package1 + slices: + myslice: + contents: + /dir/file1: + /hardlink1: + `, + "slices/mydir/test-package2.yaml": ` + package: test-package2 + slices: + myslice: + contents: + /dir/file2: + /hardlink2: + `, + }, + filesystem: map[string]string{ + "/dir/": "dir 0755", + "/dir/file1": "file 0644 28121945", + "/hardlink1": "file 0644 28121945", + "/dir/file2": "file 0644 28121945", + "/hardlink2": "file 0644 28121945", + }, + manifestPaths: map[string]string{ + "/dir/file1": "file 0644 28121945 <1> {test-package1_myslice}", + "/hardlink1": "file 0644 28121945 <1> {test-package1_myslice}", + "/dir/file2": "file 0644 28121945 <2> {test-package2_myslice}", + "/hardlink2": "file 0644 28121945 <2> {test-package2_myslice}", + }, }} var defaultChiselYaml = ` diff --git a/internal/testutil/treedump.go b/internal/testutil/treedump.go index e4dee2d4..86fbd429 100644 --- a/internal/testutil/treedump.go +++ b/internal/testutil/treedump.go @@ -75,10 +75,6 @@ func TreeDumpEntry(entry *fsutil.Entry) string { case fs.ModeSymlink: return fmt.Sprintf("symlink %s", entry.Link) case 0: - // Hard link. - if entry.Link != "" { - return fmt.Sprintf("hardlink %s", entry.Link) - } // Regular file. if entry.Size == 0 { return fmt.Sprintf("file %#o empty", entry.Mode.Perm())