Skip to content

Commit

Permalink
Include hard-linked files as hard-links in the tarstream
Browse files Browse the repository at this point in the history
Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
  • Loading branch information
TBBle committed Aug 10, 2022
1 parent 9b47fa1 commit d376404
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 0 deletions.
12 changes: 12 additions & 0 deletions internal/wclayer/baselayerreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ func (r *baseLayerReader) Next() (path string, size int64, fileInfo *winio.FileB
return
}

func (r *baseLayerReader) LinkInfo() (uint32, *winio.FileIDInfo, error) {
fileStandardInfo, err := winio.GetFileStandardInfo(r.currentFile)
if err != nil {
return 0, nil, err
}
fileIDInfo, err := winio.GetFileID(r.currentFile)
if err != nil {
return 0, nil, err
}
return fileStandardInfo.NumberOfLinks, fileIDInfo, nil
}

func (r *baseLayerReader) Read(b []byte) (int, error) {
if r.backupReader == nil {
if r.currentFile == nil {
Expand Down
2 changes: 2 additions & 0 deletions internal/wclayer/exportlayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func ExportLayer(ctx context.Context, path string, exportFolderPath string, pare
type LayerReader interface {
// Next advances to the next file and returns the name, size, and file info
Next() (string, int64, *winio.FileBasicInfo, error)
// LinkInfo returns the number of links and the file identifier for the current file.
LinkInfo() (uint32, *winio.FileIDInfo, error)
// Read reads data from the current file, in the format of a Win32 backup stream, and
// returns the number of bytes read.
Read(b []byte) (int, error)
Expand Down
12 changes: 12 additions & 0 deletions internal/wclayer/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,18 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
return
}

func (r *legacyLayerReader) LinkInfo() (uint32, *winio.FileIDInfo, error) {
fileStandardInfo, err := winio.GetFileStandardInfo(r.currentFile)
if err != nil {
return 0, nil, err
}
fileIDInfo, err := winio.GetFileID(r.currentFile)
if err != nil {
return 0, nil, err
}
return fileStandardInfo.NumberOfLinks, fileIDInfo, nil
}

func (r *legacyLayerReader) Read(b []byte) (int, error) {
if r.backupReader == nil {
if r.currentFile == nil {
Expand Down
23 changes: 23 additions & 0 deletions pkg/ociwclayer/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ func ExportLayerToTar(ctx context.Context, w io.Writer, path string, parentLayer
}

func writeTarFromLayer(ctx context.Context, r wclayer.LayerReader, w io.Writer) error {
linkRecords := make(map[[16]byte]string)

t := tar.NewWriter(w)
for {
select {
Expand All @@ -76,6 +78,27 @@ func writeTarFromLayer(ctx context.Context, r wclayer.LayerReader, w io.Writer)
return err
}
} else {
numberOfLinks, fileIDInfo, err := r.LinkInfo()
if err != nil {
return err
}
if numberOfLinks > 1 {
if linkName, ok := linkRecords[fileIDInfo.FileID]; ok {
// We've seen this file before, by another name, so put a hardlink in the tar stream.
hdr := backuptar.BasicInfoHeader(name, 0, fileInfo)
hdr.Mode = 0644
hdr.Typeflag = tar.TypeLink
hdr.Linkname = linkName
if err := t.WriteHeader(hdr); err != nil {
return err
}
continue
}

// All subsequent names for this file will be hard-linked to this name
linkRecords[fileIDInfo.FileID] = filepath.ToSlash(name)
}

err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
if err != nil {
return err
Expand Down

0 comments on commit d376404

Please sign in to comment.