1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package gitaly
import (
"embed"
"fmt"
"io"
"os"
"path/filepath"
)
// buildDir is the directory path where our build target places the built binaries.
const buildDir = "_build/bin"
// packedBinariesFS contains embedded binaries. If you modify the above embeddings, you must also update
// GITALY_PACKED_EXECUTABLES in Makefile and packedBinaries in internal/gitaly/config/config.go.
//
//go:embed _build/bin/gitaly-hooks _build/bin/gitaly-ssh _build/bin/gitaly-git2go _build/bin/gitaly-lfs-smudge
var packedBinariesFS embed.FS
// UnpackAuxiliaryBinaries unpacks the packed auxiliary binaries of Gitaly into destination directory.
//
// Gitaly invoking auxiliary binaries across different releases is a source of backwards compatibility issues.
// The calling protocol may change and cause issues if we don't carefully maintain the compatibility. Major version
// changing the module path also causes problems for gob encoding as it effectively changes the name of every type.
// To avoid having to maintain backwards compatibility between the different Gitaly binaries, we want to pin a given
// gitaly binary to only ever call the auxiliary binaries of the same build. We achieve this by packing the auxiliary
// binaries in the main gitaly binary and unpacking them on start to a temporary directory we can call them from. This
// way updating the gitaly binaries on the disk is atomic and a running gitaly can't call auxiliary binaries from a
// different version.
func UnpackAuxiliaryBinaries(destinationDir string) error {
entries, err := packedBinariesFS.ReadDir(buildDir)
if err != nil {
return fmt.Errorf("list packed binaries: %w", err)
}
for _, entry := range entries {
if err := func() error {
packedPath := filepath.Join(buildDir, entry.Name())
packedFile, err := packedBinariesFS.Open(packedPath)
if err != nil {
return fmt.Errorf("open packed binary %q: %w", packedPath, err)
}
defer func() {
// We already check the error below.
_ = packedFile.Close()
}()
unpackedPath := filepath.Join(destinationDir, entry.Name())
unpackedFile, err := os.OpenFile(unpackedPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o700)
if err != nil {
return err
}
defer func() {
// We already check the error below.
_ = unpackedFile.Close()
}()
if _, err := io.Copy(unpackedFile, packedFile); err != nil {
return fmt.Errorf("unpack %q: %w", unpackedPath, err)
}
if err := unpackedFile.Close(); err != nil {
return fmt.Errorf("close %q: %w", unpackedPath, err)
}
if err := packedFile.Close(); err != nil {
return fmt.Errorf("close packed file %q: %w", packedPath, err)
}
return nil
}(); err != nil {
return err
}
}
return nil
}
|