Skip to content

Commit

Permalink
feat(manifest): add hard link validation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
rebornplusplus authored and zhijie-yang committed Nov 28, 2024
1 parent 0a922ec commit 3a9a338
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
32 changes: 32 additions & 0 deletions internal/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ func fastValidate(options *WriteOptions) (err error) {
}
sliceExist[slice.String()] = true
}
hardLinkGroups := make(map[uint64][]*ReportEntry)
for _, entry := range options.Report.Entries {
err := validateReportEntry(&entry)
if err != nil {
Expand All @@ -344,7 +345,38 @@ func fastValidate(options *WriteOptions) (err error) {
return fmt.Errorf("path %q refers to missing slice %s", entry.Path, slice.String())
}
}
if entry.HardLinkId != NON_HARD_LINK {
e := entry
hardLinkGroups[e.HardLinkId] = append(hardLinkGroups[e.HardLinkId], &e)
}
}
// Entries within a hard link group must have same content.
var linkIDs []uint64
for id := range hardLinkGroups {
linkIDs = append(linkIDs, id)
}
sort.Slice(linkIDs, func(i, j int) bool {
return linkIDs[i] < linkIDs[j]
})
for i, id := range linkIDs {
if uint64(i+1) != id {
return fmt.Errorf("skipped hard link ID %d, but %d exists", i+1, id)
}
entries := hardLinkGroups[id]
if len(entries) == 1 {
return fmt.Errorf("hard link group %d has only one path: %s", id, entries[0].Path)
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Path < entries[j].Path
})
e0 := entries[0]
for _, e := range entries[1:] {
if e.Link != e0.Link || e.Mode != e0.Mode || e.SHA256 != e0.SHA256 || e.Size != e0.Size {
return fmt.Errorf("hard linked paths %q and %q have diverging contents", e0.Path, e.Path)
}
}
}

return nil
}

Expand Down
74 changes: 74 additions & 0 deletions internal/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,16 @@ var generateManifestTests = []struct {
Size: 1234,
Slices: map[*setup.Slice]bool{slice1: true},
FinalSHA256: "final-hash",
HardLinkId: 1,
},
"/hardlink": {
Path: "/hardlink",
Mode: 0456,
SHA256: "hash",
Size: 1234,
Slices: map[*setup.Slice]bool{slice1: true},
FinalSHA256: "final-hash",
HardLinkId: 1,
},
"/link": {
Path: "/link",
Expand Down Expand Up @@ -324,6 +334,16 @@ var generateManifestTests = []struct {
Size: 1234,
SHA256: "hash",
FinalSHA256: "final-hash",
HardLinkId: 1,
}, {
Kind: "path",
Path: "/hardlink",
Mode: "0456",
Slices: []string{"package1_slice1"},
Size: 1234,
SHA256: "hash",
FinalSHA256: "final-hash",
HardLinkId: 1,
}, {
Kind: "path",
Path: "/link",
Expand Down Expand Up @@ -355,6 +375,10 @@ var generateManifestTests = []struct {
Kind: "content",
Slice: "package1_slice1",
Path: "/file",
}, {
Kind: "content",
Slice: "package1_slice1",
Path: "/hardlink",
}, {
Kind: "content",
Slice: "package1_slice1",
Expand Down Expand Up @@ -539,6 +563,56 @@ var generateManifestTests = []struct {
},
},
error: `internal error: invalid manifest: path "/dir" has invalid options: size set for directory`,
}, {
summary: "Invalid path: skipped hard link ID",
report: &manifest.Report{
Root: "/",
Entries: map[string]manifest.ReportEntry{
"/file": {
Path: "/file",
Slices: map[*setup.Slice]bool{slice1: true},
HardLinkId: 2,
},
},
},
error: `internal error: invalid manifest: skipped hard link ID 1, but 2 exists`,
}, {
summary: "Invalid path: hard link group has only one path",
report: &manifest.Report{
Root: "/",
Entries: map[string]manifest.ReportEntry{
"/file": {
Path: "/file",
Slices: map[*setup.Slice]bool{slice1: true},
HardLinkId: 1,
},
},
},
error: `internal error: invalid manifest: hard link group 1 has only one path: /file`,
}, {
summary: "Invalid path: hard linked paths differ",
report: &manifest.Report{
Root: "/",
Entries: map[string]manifest.ReportEntry{
"/file": {
Path: "/file",
Mode: 0456,
SHA256: "hash",
Size: 1234,
Slices: map[*setup.Slice]bool{slice1: true},
HardLinkId: 1,
},
"/hardlink": {
Path: "/hardlink",
Mode: 0456,
SHA256: "different-hash",
Size: 1234,
Slices: map[*setup.Slice]bool{slice1: true},
HardLinkId: 1,
},
},
},
error: `internal error: invalid manifest: hard linked paths "/file" and "/hardlink" have diverging contents`,
}, {
summary: "Invalid package: missing name",
packageInfo: []*archive.PackageInfo{{
Expand Down

0 comments on commit 3a9a338

Please sign in to comment.