# This is a regression test for #652435. It checks that we don't generate # multiple action entry ids for the same index file. That was happening # previously because we sometimes generated the action id with unclean # paths (and the rest of the time with clean paths) for the same package. # This test will use a go program ('check') to check the cache that there are # no two action entry files that point to the same object id. [short] skip 'builds and runs a go program' sleep 2s # Sleep so that the unpacked files are > 2 seconds old. The index won't be used if the modified times on the files are newer. go build -o check$GOEXE check.go cd mod env GOCACHE=$WORK/newcache # Run list command in a clean cache. go list all exec ../check$GOEXE -- mod/go.mod -- module example.com/foo -- mod/foo.go -- package foo -- check.go -- package main import ( "errors" "log" "os" "path/filepath" "strings" ) func main() { cachedir := os.Getenv("GOCACHE") if cachedir == "" { log.Fatal("GOCACHE env var is empty; expected it to be set") } // Read the top level cache directory. The cache directory contains directories with // each of the possible two hex digit prefixes (00-FF) of a cache entry's id. // Those directories in turn contain files with the hex id followed by either // "-a" for the action entries or "-d" for the object entries. We want to check // if two action entries point to the same object entry so we'll iterate through // all the "-a" files and see if any two of them refer to the same object id // (corresponding to a "-d" file). dirs, err := os.ReadDir(cachedir) if err != nil { log.Fatal(err) } seen := map[string]string{} // object id -> action id for _, entry := range dirs { if entry.IsDir() && len(entry.Name()) == 2 { prefixdir := filepath.Join(cachedir, entry.Name()) entries, err := os.ReadDir(prefixdir) if err != nil { log.Fatal(err) } for _, entry := range entries { if !strings.HasSuffix(entry.Name(), "-a") { // not an action id entry continue } actionEntryFile := filepath.Join(prefixdir, entry.Name()) objid, err := objectid(actionEntryFile) if err != nil { log.Fatal(err) } if other, ok := seen[objid]; ok { log.Printf("found two action entry files (%s, %s) pointing to the same object id: %s", other, entry.Name(), objid) os.Exit(1) } seen[objid] = entry.Name() } } } } // objectid returns the object id that the given actionEntryFile points to. func objectid(actionEntryFile string) (string, error) { // See cmd/go/internal/cache.(*DiskCache).get for the code that reads // from the action entry files. The following is based on that function. const ( HashSize = 32 hexSize = HashSize * 2 entrySize = 2 + 1 + hexSize + 1 + hexSize + 1 + 20 + 1 + 20 + 1 ) entry, err := os.ReadFile(actionEntryFile) if err != nil { return "", err } if len(entry) < entrySize { return "", errors.New("entry file incomplete") } if entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\n' { return "", errors.New("invalid header") } return string(entry[3+hexSize+1 : 3+hexSize+1+hexSize]), nil }