aboutsummaryrefslogtreecommitdiff
path: root/format/pack/ingest/finalize.go
blob: 9b04a7c11ae374ec21c699d1400cf5c168b96428 (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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package ingest

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

	"codeberg.org/lindenii/furgit/internal/intconv"
)

// 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"
	}

	err := linkTempToFinal(state, state.packTmpName, packFinal)
	if err != nil {
		return Result{}, err
	}

	err = linkTempToFinal(state, state.idxTmpName, idxFinal)
	if err != nil {
		return Result{}, err
	}

	if state.writeRev {
		err := linkTempToFinal(state, state.revTmpName, revFinal)
		if err != nil {
			return Result{}, err
		}
	}

	objectCount, err := intconv.IntToUint32(len(state.records))
	if err != nil {
		return Result{}, err
	}

	return Result{
		PackName:    packFinal,
		IdxName:     idxFinal,
		RevName:     revFinal,
		PackHash:    state.packHash,
		ObjectCount: objectCount,
		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
}