aboutsummaryrefslogtreecommitdiff
path: root/format/pack/ingest/finalize.go
blob: 06b30102e28b03aa0a56f0fa679c848a4b972ba0 (about) (plain) (blame)
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 ingest

import (
	"errors"
	"fmt"
	"io/fs"
	"strings"
)

// finalizeArtifacts links temporary files to final names and returns Result.
func finalizeArtifacts(state *ingestState) (Result, error) {
	base := "pack-" + state.packHash.String()
	packFinal := base + ".pack"
	idxFinal := base + ".idx"
	revFinal := ""
	if state.writeRev {
		revFinal = base + ".rev"
	}

	if err := linkTempToFinal(state, state.packTmpName, packFinal); err != nil {
		return Result{}, err
	}
	if err := linkTempToFinal(state, state.idxTmpName, idxFinal); err != nil {
		return Result{}, err
	}
	if state.writeRev {
		if err := linkTempToFinal(state, state.revTmpName, revFinal); err != nil {
			return Result{}, err
		}
	}

	return Result{
		PackName:    packFinal,
		IdxName:     idxFinal,
		RevName:     revFinal,
		PackHash:    state.packHash,
		ObjectCount: uint32(len(state.records)),
		ThinFixed:   state.thinFixed,
	}, nil
}

// rollbackTemporaryArtifacts removes temporary files after failure.
func rollbackTemporaryArtifacts(state *ingestState) {
	if state.packTmpName != "" {
		_ = state.destination.Remove(state.packTmpName)
	}
	if state.idxTmpName != "" {
		_ = state.destination.Remove(state.idxTmpName)
	}
	if state.revTmpName != "" {
		_ = state.destination.Remove(state.revTmpName)
	}
}

// linkTempToFinal hard-links tmp to final, tolerating existing final paths.
func linkTempToFinal(state *ingestState, tmp, final string) error {
	if tmp == "" || final == "" {
		return fmt.Errorf("format/pack/ingest: invalid finalize names tmp=%q final=%q", tmp, final)
	}
	if strings.Contains(final, "/") {
		return fmt.Errorf("format/pack/ingest: final name must be leaf: %q", final)
	}

	err := state.destination.Link(tmp, final)
	if err == nil {
		_ = state.destination.Remove(tmp)

		return nil
	}
	if errors.Is(err, fs.ErrExist) {
		_ = state.destination.Remove(tmp)

		return nil
	}

	return err
}