Skip to content

Commit

Permalink
chore: apply changes from prelim code review
Browse files Browse the repository at this point in the history
  • Loading branch information
zhijie-yang committed Nov 19, 2024
1 parent e988817 commit de42bf2
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 40 deletions.
10 changes: 7 additions & 3 deletions internal/deb/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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,
}
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion internal/fsutil/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
89 changes: 53 additions & 36 deletions internal/manifest/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -54,60 +56,71 @@ 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
} else {
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())
Expand Down Expand Up @@ -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)
}

0 comments on commit de42bf2

Please sign in to comment.