Skip to content

Commit

Permalink
Include repeated 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 Jan 10, 2021
1 parent 8816393 commit 014672b
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 0 deletions.
23 changes: 23 additions & 0 deletions internal/ociwclayer/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func ExportLayer(w io.Writer, path string, parentLayerPaths []string) error {
}

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

t := tar.NewWriter(w)
for {
name, size, fileInfo, err := r.Next()
Expand All @@ -69,6 +71,27 @@ func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error {
return err
}
} else {
fileIDInfo, err := r.LinkInfo()
if err != nil {
return err
}
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
// Optimisation opportunity: Only store records for files with multiple links.
// e.g., go-winio could expose FileStandardInfo to enable this.
linkRecords[fileIDInfo.FileID] = filepath.ToSlash(name)

err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions internal/wclayer/baselayerreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ func (r *baseLayerReader) Next() (path string, size int64, fileInfo *winio.FileB
return
}

func (r *baseLayerReader) LinkInfo() (fileIDInfo *winio.FileIDInfo, err error) {
fileIDInfo, err = winio.GetFileID(r.currentFile)
return
}

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 @@ -44,6 +44,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 file identifier for the current file.
LinkInfo() (*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
5 changes: 5 additions & 0 deletions internal/wclayer/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
return
}

func (r *legacyLayerReader) LinkInfo() (fileIDInfo *winio.FileIDInfo, err error) {
fileIDInfo, err = winio.GetFileID(r.currentFile)
return
}

func (r *legacyLayerReader) Read(b []byte) (int, error) {
if r.backupReader == nil {
if r.currentFile == nil {
Expand Down

0 comments on commit 014672b

Please sign in to comment.