Read quarantine evidence back when planning a seed
This commit is contained in:
@@ -23,6 +23,11 @@ type ZoneDiskState struct {
|
||||
JournalBegin int64
|
||||
JournalEnd int64
|
||||
JournalOK bool
|
||||
// OrphanSerial is the furthest serial recorded in a quarantine sibling on
|
||||
// disk. It is the only record of how far the zone had advanced once a
|
||||
// transition is interrupted between the renames and the new file landing.
|
||||
OrphanSerial int64
|
||||
OrphanSerialOK bool
|
||||
}
|
||||
|
||||
// SeedPlan is the decision taken before writing a skeleton zone file.
|
||||
@@ -92,12 +97,52 @@ func highestSerial(st ZoneDiskState) (int64, bool) {
|
||||
if st.Journal && st.JournalOK && (!known || serialLT(h, st.JournalEnd)) {
|
||||
h, known = st.JournalEnd, true
|
||||
}
|
||||
if st.OrphanSerialOK && (!known || serialLT(h, st.OrphanSerial)) {
|
||||
h, known = st.OrphanSerial, true
|
||||
}
|
||||
return h, known
|
||||
}
|
||||
|
||||
func quarantineSuffix(st ZoneDiskState) string {
|
||||
h, _ := highestSerial(st)
|
||||
return fmt.Sprintf(".orphaned-%d", h)
|
||||
return quarantineMarker + strconv.FormatInt(h, 10)
|
||||
}
|
||||
|
||||
// parseOrphanSerial reads the serial back out of a name moveAside produced,
|
||||
// with or without the counter it appends when the destination is taken.
|
||||
func parseOrphanSerial(name string) (int64, bool) {
|
||||
i := strings.LastIndex(name, quarantineMarker)
|
||||
if i < 0 {
|
||||
return 0, false
|
||||
}
|
||||
digits := name[i+len(quarantineMarker):]
|
||||
n := 0
|
||||
for n < len(digits) && digits[n] >= '0' && digits[n] <= '9' {
|
||||
n++
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, false
|
||||
}
|
||||
serial, err := strconv.ParseUint(digits[:n], 10, 32)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return int64(serial), true
|
||||
}
|
||||
|
||||
func orphanSerial(names []string) (int64, bool) {
|
||||
var h int64
|
||||
var known bool
|
||||
for _, name := range names {
|
||||
s, ok := parseOrphanSerial(name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !known || serialLT(h, s) {
|
||||
h, known = s, true
|
||||
}
|
||||
}
|
||||
return h, known
|
||||
}
|
||||
|
||||
func nextSerial(st ZoneDiskState) int64 {
|
||||
@@ -199,10 +244,14 @@ func beUint32(b []byte) uint32 {
|
||||
// stdout that was truncated, empty or partially written is rejected rather than
|
||||
// read as "fresh install".
|
||||
const (
|
||||
probeBegin = "zonestate-begin-v1"
|
||||
probeEnd = "zonestate-end-v1"
|
||||
probeHeadOpen = "head<<"
|
||||
probeHeadShut = ">>head"
|
||||
probeBegin = "zonestate-begin-v1"
|
||||
probeEnd = "zonestate-end-v1"
|
||||
probeHeadOpen = "head<<"
|
||||
probeHeadShut = ">>head"
|
||||
probeOrphanOpen = "orphans<<"
|
||||
probeOrphanShut = ">>orphans"
|
||||
// quarantineMarker joins a preserved file to the serial it was holding.
|
||||
quarantineMarker = ".orphaned-"
|
||||
)
|
||||
|
||||
// zoneStateProbe reads only the head of the zone file: the SOA is the first
|
||||
@@ -215,6 +264,10 @@ func zoneStateProbe(path string) string {
|
||||
zone, probeHeadOpen, zone, probeHeadShut),
|
||||
fmt.Sprintf("if [ -f %s ]; then printf 'journal=1\\n'; hdr=$(od -An -v -tx1 -N32 %s) || exit 1; printf 'jnl=%%s\\n' \"$(printf '%%s' \"$hdr\" | tr -d ' \\n')\"; else printf 'journal=0\\n'; fi",
|
||||
jnl, jnl),
|
||||
// The quarantine siblings outlive the files they replaced, so they are
|
||||
// the floor a reseed must stay above after an interrupted transition.
|
||||
fmt.Sprintf("printf '%s\\n'\nfor f in %s* %s*; do if [ -e \"$f\" ]; then printf '%%s\\n' \"$f\"; fi; done\nprintf '%s\\n'",
|
||||
probeOrphanOpen, shellQuote(path+quarantineMarker), shellQuote(JournalPath(path)+quarantineMarker), probeOrphanShut),
|
||||
fmt.Sprintf("printf '%s\\n'", probeEnd),
|
||||
}, "\n")
|
||||
}
|
||||
@@ -223,9 +276,9 @@ func zoneStateProbe(path string) string {
|
||||
// half-written or empty probe must never be mistaken for an empty filesystem.
|
||||
func parseZoneDiskState(out string) (ZoneDiskState, bool) {
|
||||
var st ZoneDiskState
|
||||
var head []string
|
||||
var sawBegin, sawEnd, inHead, headShut bool
|
||||
var zoneDecls, journalDecls, headerDecls int
|
||||
var head, orphans []string
|
||||
var sawBegin, sawEnd, inHead, headShut, inOrphans, orphansShut bool
|
||||
var zoneDecls, journalDecls, headerDecls, orphanDecls int
|
||||
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
switch {
|
||||
@@ -233,12 +286,20 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
|
||||
inHead, headShut = false, true
|
||||
case inHead:
|
||||
head = append(head, line)
|
||||
case inOrphans && line == probeOrphanShut:
|
||||
inOrphans, orphansShut = false, true
|
||||
case inOrphans:
|
||||
if line != "" {
|
||||
orphans = append(orphans, line)
|
||||
}
|
||||
case line == probeBegin:
|
||||
sawBegin = true
|
||||
case line == probeEnd:
|
||||
sawEnd = true
|
||||
case line == probeHeadOpen:
|
||||
inHead = true
|
||||
case line == probeOrphanOpen:
|
||||
inOrphans, orphanDecls = true, orphanDecls+1
|
||||
case line == "zonefile=1":
|
||||
st.ZoneFile = true
|
||||
zoneDecls++
|
||||
@@ -258,7 +319,9 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case !sawBegin || !sawEnd || inHead:
|
||||
case !sawBegin || !sawEnd || inHead || inOrphans:
|
||||
return ZoneDiskState{}, false
|
||||
case orphanDecls != 1 || !orphansShut:
|
||||
return ZoneDiskState{}, false
|
||||
case zoneDecls != 1 || journalDecls != 1:
|
||||
return ZoneDiskState{}, false
|
||||
@@ -273,6 +336,7 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
|
||||
if st.ZoneFile {
|
||||
st.ZoneSerial, st.ZoneSerialOK = ParseZoneSerial(strings.Join(head, "\n"))
|
||||
}
|
||||
st.OrphanSerial, st.OrphanSerialOK = orphanSerial(orphans)
|
||||
return st, true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user