Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions ext4/internal/compactext4/compact.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ func (w *Writer) Create(name string, f *File) error {
}

// Link adds a hard link to the file system.
// We support creating hardlinks to symlinks themselves instead of what
// the symlinks link to, as this is what containerd does upstream.
func (w *Writer) Link(oldname, newname string) error {
if err := w.finishInode(); err != nil {
return err
Expand All @@ -620,8 +622,8 @@ func (w *Writer) Link(oldname, newname string) error {
return err
}
switch oldfile.Mode & format.TypeMask {
case format.S_IFDIR, format.S_IFLNK:
return fmt.Errorf("%s: link target cannot be a directory or symlink: %s", newname, oldname)
case format.S_IFDIR:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So with this change we are setting our policy as "hard links to symlinks will point to symlink itself than the file which symlink points to". Do we know if containerd upstream also follows the same policy?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

@ambarve ambarve Oct 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thanks!

Can we add a comment here saying that we follow containerd upstream policy and provide this link? It would help in the future.

return fmt.Errorf("%s: link target cannot be a directory: %s", newname, oldname)
}

if existing != oldfile && oldfile.LinkCount >= format.MaxLinks {
Expand Down
73 changes: 73 additions & 0 deletions ext4/tar2ext4/tar2ext4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,76 @@ func Test_UnorderedTarExpansion(t *testing.T) {
t.Fatalf("failed to convert tar to layer vhd: %s", err)
}
}

func Test_TarHardlinkToSymlink(t *testing.T) {
tmpTarFilePath := filepath.Join(os.TempDir(), "test-layer.tar")
layerTar, err := os.Create(tmpTarFilePath)
if err != nil {
t.Fatalf("failed to create output file: %s", err)
}
defer os.Remove(tmpTarFilePath)

tw := tar.NewWriter(layerTar)

var files = []struct {
path string
typeFlag byte
linkName string
body string
}{
{
path: "/tmp/zzz.txt",
body: "inside /tmp/zzz.txt",
},
{
path: "/tmp/xxx.txt",
linkName: "/tmp/zzz.txt",
typeFlag: tar.TypeSymlink,
},
{
path: "/tmp/yyy.txt",
linkName: "/tmp/xxx.txt",
typeFlag: tar.TypeLink,
},
}
for _, file := range files {
hdr := &tar.Header{
Name: file.path,
Typeflag: file.typeFlag,
Linkname: file.linkName,
Mode: 0777,
Size: int64(len(file.body)),
ModTime: time.Now(),
AccessTime: time.Now(),
ChangeTime: time.Now(),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if file.body != "" {
if _, err := tw.Write([]byte(file.body)); err != nil {
t.Fatal(err)
}
}
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}

// Now try to import this tar and verify that there is no failure.
if _, err := layerTar.Seek(0, 0); err != nil {
t.Fatalf("failed to seek file: %s", err)
}

opts := []Option{AppendVhdFooter, ConvertWhiteout}
tmpVhdPath := filepath.Join(os.TempDir(), "test-vhd.vhdx")
layerVhd, err := os.Create(tmpVhdPath)
if err != nil {
t.Fatalf("failed to create output VHD: %s", err)
}
defer os.Remove(tmpVhdPath)

if err := Convert(layerTar, layerVhd, opts...); err != nil {
t.Fatalf("failed to convert tar to layer vhd: %s", err)
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.