From de42bf206cfd079b43510c8e094f682806a727e3 Mon Sep 17 00:00:00 2001 From: Zhijie Yang Date: Tue, 19 Nov 2024 09:54:22 +0100 Subject: [PATCH] chore: apply changes from prelim code review --- internal/deb/extract.go | 10 +++-- internal/fsutil/create.go | 10 ++++- internal/manifest/report.go | 89 ++++++++++++++++++++++--------------- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/internal/deb/extract.go b/internal/deb/extract.go index 0645fb7e..16b1cecb 100644 --- a/internal/deb/extract.go +++ b/internal/deb/extract.go @@ -276,7 +276,7 @@ func extractData(pkgReader io.ReadSeeker, options *ExtractOptions) error { } err := options.Create(extractInfos, createOptions) if err != nil { - // Handle the hardlink where its counterpart is not extracted + // Handle the hard link where its counterpart is not extracted if tarHeader.Typeflag == tar.TypeLink && strings.HasPrefix(err.Error(), "link target does not exist") { basePath := sanitizePath(tarHeader.Linkname) pendingHardlinks[basePath] = append(pendingHardlinks[basePath], @@ -352,9 +352,11 @@ func handlePendingHardlinks(options *ExtractOptions, pendingHardlinks map[string continue } + targetPath := hardlinks[0].TargetPath + extractPath := filepath.Join(options.TargetDir, targetPath) // Write the content for the first file in the hard link group createOption := &fsutil.CreateOptions{ - Path: filepath.Join(options.TargetDir, hardlinks[0].TargetPath), + Path: extractPath, Mode: tarHeader.FileInfo().Mode(), Data: tarReader, } @@ -364,18 +366,20 @@ func handlePendingHardlinks(options *ExtractOptions, pendingHardlinks map[string return err } delete(*pendingPaths, sourcePath) + delete(*pendingPaths, targetPath) // Create the hard links for the rest of the group for _, hardlink := range hardlinks[1:] { createOption := &fsutil.CreateOptions{ Path: filepath.Join(options.TargetDir, hardlink.TargetPath), Mode: tarHeader.FileInfo().Mode(), - Link: filepath.Join(options.TargetDir, hardlinks[0].TargetPath), + Link: extractPath, } err := options.Create(hardlink.ExtractInfos, createOption) if err != nil { return err } + delete(*pendingPaths, hardlink.TargetPath) } } return nil diff --git a/internal/fsutil/create.go b/internal/fsutil/create.go index 5d7c2d8d..91b247b7 100644 --- a/internal/fsutil/create.go +++ b/internal/fsutil/create.go @@ -163,7 +163,7 @@ func createHardLink(o *CreateOptions) error { debugf("Creating hard link: %s => %s", o.Path, o.Link) linkInfo, err := os.Lstat(o.Link) if err != nil && os.IsNotExist(err) { - return fmt.Errorf("link target does not exist: %s", o.Link) + return &LinkTargetNotExistError{Link: o.Link} } else if err != nil { return err } @@ -223,3 +223,11 @@ func (rp *writerProxy) Close() error { rp.entry.Size = rp.size return rp.inner.Close() } + +type LinkTargetNotExistError struct { + Link string +} + +func (e *LinkTargetNotExistError) Error() string { + return fmt.Sprintf("link target does not exist: %s", e.Link) +} diff --git a/internal/manifest/report.go b/internal/manifest/report.go index c482641b..13f6d28b 100644 --- a/internal/manifest/report.go +++ b/internal/manifest/report.go @@ -31,6 +31,8 @@ type Report struct { currHardLinkId int } +const NON_HARDLINK = 0 + // NewReport returns an empty report for content that will be based at the // provided root path. func NewReport(root string) (*Report, error) { @@ -54,43 +56,17 @@ func (r *Report) Add(slice *setup.Slice, fsEntry *fsutil.Entry) error { return fmt.Errorf("cannot add path to report: %s", err) } - // Handle the hard link group - hardLinkId := 0 - sha256 := fsEntry.SHA256 - size := fsEntry.Size - link := fsEntry.Link - if link != "" { - // Having the link target in root is a necessary but insufficient condition for a hardlink. - if strings.HasPrefix(fsEntry.Link, r.Root) { - relLinkPath, _ := r.sanitizeAbsPath(fsEntry.Link, false) - // With this, a hardlink is found - if entry, ok := r.Entries[relLinkPath]; ok { - if entry.HardLinkId == 0 { - r.currHardLinkId++ - entry.HardLinkId = r.currHardLinkId - r.Entries[relLinkPath] = entry - } - hardLinkId = entry.HardLinkId - if fsEntry.Mode.IsRegular() { // If the hardlink links to a regular file - sha256 = entry.SHA256 - size = entry.Size - link = "" - } else { // If the hardlink links to a symlink - link = entry.Link - } - } - } // else, this is a symlink - } + fsEntry, hardLinkId := r.handleHardlinkReport(fsEntry) if entry, ok := r.Entries[relPath]; ok { if fsEntry.Mode != entry.Mode { return fmt.Errorf("path %s reported twice with diverging mode: 0%03o != 0%03o", relPath, fsEntry.Mode, entry.Mode) - } else if link != entry.Link { - return fmt.Errorf("path %s reported twice with diverging link: %q != %q", relPath, link, entry.Link) - } else if size != entry.Size { - return fmt.Errorf("path %s reported twice with diverging size: %d != %d", relPath, size, entry.Size) - } else if sha256 != entry.SHA256 { - return fmt.Errorf("path %s reported twice with diverging hash: %q != %q", relPath, sha256, entry.SHA256) + } else if fsEntry.Link != entry.Link { + return fmt.Errorf("path %s reported twice with diverging link: %q != %q", relPath, fsEntry.Link, entry.Link) + } else if fsEntry.Size != entry.Size { + return fmt.Errorf("path %s reported twice with diverging size: %d != %d", relPath, fsEntry.Size, entry.Size) + } else if fsEntry.SHA256 != entry.SHA256 { + return fmt.Errorf("path %s reported twice with diverging hash: %q != %q", relPath, fsEntry.SHA256, entry.SHA256) } entry.Slices[slice] = true r.Entries[relPath] = entry @@ -98,16 +74,53 @@ func (r *Report) Add(slice *setup.Slice, fsEntry *fsutil.Entry) error { r.Entries[relPath] = ReportEntry{ Path: relPath, Mode: fsEntry.Mode, - SHA256: sha256, - Size: size, + SHA256: fsEntry.SHA256, + Size: fsEntry.Size, Slices: map[*setup.Slice]bool{slice: true}, - Link: link, + Link: fsEntry.Link, HardLinkId: hardLinkId, } } return nil } +func (r *Report) handleHardlinkReport(fsEntry *fsutil.Entry) (*fsutil.Entry, int) { + // Handle the hard link group + hardLinkId := NON_HARDLINK + sha256 := fsEntry.SHA256 + size := fsEntry.Size + link := fsEntry.Link + if r.entryIsHardLink(fsEntry.Link) { + relLinkPath, _ := r.sanitizeAbsPath(fsEntry.Link, false) + // With this, a hardlink is found + if entry, ok := r.Entries[relLinkPath]; ok { + if entry.HardLinkId == NON_HARDLINK { + r.currHardLinkId++ + entry.HardLinkId = r.currHardLinkId + r.Entries[relLinkPath] = entry + } + hardLinkId = entry.HardLinkId + if fsEntry.Mode.IsRegular() { + // The hard link links to a regular file + sha256 = entry.SHA256 + size = entry.Size + link = "" + } else { + // The hard link links to a symlink + link = entry.Link + } + } + } + + return &fsutil.Entry{ + Path: fsEntry.Path, + Mode: fsEntry.Mode, + SHA256: sha256, + Size: size, + Link: link, + }, hardLinkId +} + // Mutate updates the FinalSHA256 and Size of an existing path entry. func (r *Report) Mutate(fsEntry *fsutil.Entry) error { relPath, err := r.sanitizeAbsPath(fsEntry.Path, fsEntry.Mode.IsDir()) @@ -142,3 +155,7 @@ func (r *Report) sanitizeAbsPath(path string, isDir bool) (relPath string, err e } return relPath, nil } + +func (r *Report) entryIsHardLink(link string) bool { + return link != "" && strings.HasPrefix(link, r.Root) +}