| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build ignore |
| |
| // Mkzip creates a FIPS snapshot zip file. |
| // See GOROOT/lib/fips140/README.md and GOROOT/lib/fips140/Makefile |
| // for more details about when and why to use this. |
| // |
| // Usage: |
| // |
| // cd GOROOT/lib/fips140 |
| // go run ../../src/cmd/go/internal/fips140/mkzip.go [-b branch] v1.2.3 |
| // |
| // Mkzip creates a zip file named for the version on the command line |
| // using the sources in the named branch (default origin/master, |
| // to avoid accidentally including local commits). |
| package main |
| |
| import ( |
| "archive/zip" |
| "bytes" |
| "flag" |
| "fmt" |
| "io" |
| "log" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "golang.org/x/mod/module" |
| "golang.org/x/mod/semver" |
| modzip "golang.org/x/mod/zip" |
| ) |
| |
| var flagBranch = flag.String("b", "origin/master", "branch to use") |
| |
| func usage() { |
| fmt.Fprintf(os.Stderr, "usage: go run mkzip.go [-b branch] vX.Y.Z\n") |
| os.Exit(2) |
| } |
| |
| func main() { |
| log.SetFlags(0) |
| log.SetPrefix("mkzip: ") |
| flag.Usage = usage |
| flag.Parse() |
| if flag.NArg() != 1 { |
| usage() |
| } |
| |
| // Must run in the lib/fips140 directory, where the snapshots live. |
| wd, err := os.Getwd() |
| if err != nil { |
| log.Fatal(err) |
| } |
| if !strings.HasSuffix(filepath.ToSlash(wd), "lib/fips140") { |
| log.Fatalf("must be run in lib/fips140 directory") |
| } |
| |
| // Must have valid version, and must not overwrite existing file. |
| version := flag.Arg(0) |
| if semver.Canonical(version) != version { |
| log.Fatalf("invalid version %q; must be vX.Y.Z", version) |
| } |
| if _, err := os.Stat(version + ".zip"); err == nil { |
| log.Fatalf("%s.zip already exists", version) |
| } |
| |
| // Make standard module zip file in memory. |
| // The module path "golang.org/fips140" needs to be a valid module name, |
| // and it is the path where the zip file will be unpacked in the module cache. |
| // The path must begin with a domain name to satisfy the module validation rules, |
| // but otherwise the path is not used. The cmd/go code using these zips |
| // knows that the zip contains crypto/internal/fips140. |
| goroot := "../.." |
| var zbuf bytes.Buffer |
| err = modzip.CreateFromVCS(&zbuf, |
| module.Version{Path: "golang.org/fips140", Version: version}, |
| goroot, *flagBranch, "src/crypto/internal/fips140") |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| // Write new zip file with longer paths: fips140/v1.2.3/foo.go instead of foo.go. |
| // That way we can bind the fips140 directory onto the |
| // GOROOT/src/crypto/internal/fips140 directory and get a |
| // crypto/internal/fips140/v1.2.3 with the snapshot code |
| // and an otherwise empty crypto/internal/fips140 directory. |
| zr, err := zip.NewReader(bytes.NewReader(zbuf.Bytes()), int64(zbuf.Len())) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| var zbuf2 bytes.Buffer |
| zw := zip.NewWriter(&zbuf2) |
| foundVersion := false |
| for _, f := range zr.File { |
| // golang.org/fips140@v1.2.3/dir/file.go -> |
| // golang.org/fips140@v1.2.3/fips140/v1.2.3/dir/file.go |
| if f.Name != "golang.org/fips140@"+version+"/LICENSE" { |
| f.Name = "golang.org/fips140@" + version + "/fips140/" + version + |
| strings.TrimPrefix(f.Name, "golang.org/fips140@"+version) |
| } |
| // Inject version in [crypto/internal/fips140.Version]. |
| if f.Name == "golang.org/fips140@"+version+"/fips140/"+version+"/fips140.go" { |
| rf, err := f.Open() |
| if err != nil { |
| log.Fatal(err) |
| } |
| contents, err := io.ReadAll(rf) |
| if err != nil { |
| log.Fatal(err) |
| } |
| returnLine := `return "latest" //mkzip:version` |
| if !bytes.Contains(contents, []byte(returnLine)) { |
| log.Fatalf("did not find %q in fips140.go", returnLine) |
| } |
| // Use only the vX.Y.Z part of a possible vX.Y.Z-hash version. |
| v, _, _ := strings.Cut(version, "-") |
| newLine := `return "` + v + `"` |
| contents = bytes.ReplaceAll(contents, []byte(returnLine), []byte(newLine)) |
| wf, err := zw.Create(f.Name) |
| if err != nil { |
| log.Fatal(err) |
| } |
| if _, err := wf.Write(contents); err != nil { |
| log.Fatal(err) |
| } |
| foundVersion = true |
| continue |
| } |
| wf, err := zw.CreateRaw(&f.FileHeader) |
| if err != nil { |
| log.Fatal(err) |
| } |
| rf, err := f.OpenRaw() |
| if err != nil { |
| log.Fatal(err) |
| } |
| if _, err := io.Copy(wf, rf); err != nil { |
| log.Fatal(err) |
| } |
| } |
| if err := zw.Close(); err != nil { |
| log.Fatal(err) |
| } |
| if !foundVersion { |
| log.Fatal("did not find fips140.go file") |
| } |
| |
| err = os.WriteFile(version+".zip", zbuf2.Bytes(), 0666) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| log.Printf("wrote %s.zip", version) |
| } |