| package archive // import "github.com/docker/docker/pkg/archive" | |
| import ( | |
| "archive/tar" | |
| "fmt" | |
| "io" | |
| "os" | |
| "path" | |
| "sort" | |
| "testing" | |
| "github.com/docker/docker/pkg/idtools" | |
| ) | |
| func TestHardLinkOrder(t *testing.T) { | |
| names := []string{"file1.txt", "file2.txt", "file3.txt"} | |
| msg := []byte("Hey y'all") | |
| // Create dir | |
| src, err := os.MkdirTemp("", "docker-hardlink-test-src-") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer os.RemoveAll(src) | |
| for _, name := range names { | |
| func() { | |
| err := os.WriteFile(path.Join(src, name), msg, 0o666) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| }() | |
| } | |
| // Create dest, with changes that includes hardlinks | |
| dest, err := os.MkdirTemp("", "docker-hardlink-test-dest-") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| os.RemoveAll(dest) // we just want the name, at first | |
| if err := copyDir(src, dest); err != nil { | |
| t.Fatal(err) | |
| } | |
| defer os.RemoveAll(dest) | |
| for _, name := range names { | |
| for i := 0; i < 5; i++ { | |
| if err := os.Link(path.Join(dest, name), path.Join(dest, fmt.Sprintf("%s.link%d", name, i))); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| } | |
| // get changes | |
| changes, err := ChangesDirs(dest, src) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // sort | |
| sort.Sort(changesByPath(changes)) | |
| // ExportChanges | |
| ar, err := ExportChanges(dest, changes, idtools.IdentityMapping{}) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| hdrs, err := walkHeaders(ar) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // reverse sort | |
| sort.Sort(sort.Reverse(changesByPath(changes))) | |
| // ExportChanges | |
| arRev, err := ExportChanges(dest, changes, idtools.IdentityMapping{}) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| hdrsRev, err := walkHeaders(arRev) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // line up the two sets | |
| sort.Sort(tarHeaders(hdrs)) | |
| sort.Sort(tarHeaders(hdrsRev)) | |
| // compare Size and LinkName | |
| for i := range hdrs { | |
| if hdrs[i].Name != hdrsRev[i].Name { | |
| t.Errorf("headers - expected name %q; but got %q", hdrs[i].Name, hdrsRev[i].Name) | |
| } | |
| if hdrs[i].Size != hdrsRev[i].Size { | |
| t.Errorf("headers - %q expected size %d; but got %d", hdrs[i].Name, hdrs[i].Size, hdrsRev[i].Size) | |
| } | |
| if hdrs[i].Typeflag != hdrsRev[i].Typeflag { | |
| t.Errorf("headers - %q expected type %d; but got %d", hdrs[i].Name, hdrs[i].Typeflag, hdrsRev[i].Typeflag) | |
| } | |
| if hdrs[i].Linkname != hdrsRev[i].Linkname { | |
| t.Errorf("headers - %q expected linkname %q; but got %q", hdrs[i].Name, hdrs[i].Linkname, hdrsRev[i].Linkname) | |
| } | |
| } | |
| } | |
| type tarHeaders []tar.Header | |
| func (th tarHeaders) Len() int { return len(th) } | |
| func (th tarHeaders) Swap(i, j int) { th[j], th[i] = th[i], th[j] } | |
| func (th tarHeaders) Less(i, j int) bool { return th[i].Name < th[j].Name } | |
| func walkHeaders(r io.Reader) ([]tar.Header, error) { | |
| t := tar.NewReader(r) | |
| var headers []tar.Header | |
| for { | |
| hdr, err := t.Next() | |
| if err != nil { | |
| if err == io.EOF { | |
| break | |
| } | |
| return headers, err | |
| } | |
| headers = append(headers, *hdr) | |
| } | |
| return headers, nil | |
| } |