mirror of
https://github.com/vbatts/go-mtree.git
synced 2025-09-13 22:03:19 +00:00
compare: improve tar_time truncation
Rather than parsing the value as a float and then truncating it, just parse it as an integer in the first place (this also adds some validation that we are parsing a reasonable-looking value). While we're at it, add some integration tests for this code to make sure this quite complicated special-case behaviour doesn't regress. Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
parent
0beb885cbf
commit
604ab42863
2 changed files with 116 additions and 9 deletions
29
compare.go
29
compare.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// XXX: Do we need a Difference interface to make it so people can do var x
|
// XXX: Do we need a Difference interface to make it so people can do var x
|
||||||
|
@ -253,23 +252,35 @@ func compareEntry(oldEntry, newEntry Entry) ([]KeyDelta, error) {
|
||||||
|
|
||||||
// Make a new tar_time.
|
// Make a new tar_time.
|
||||||
if diffs["tar_time"].Old == nil {
|
if diffs["tar_time"].Old == nil {
|
||||||
time, err := strconv.ParseFloat(timeStateT.Old.Value(), 64)
|
var (
|
||||||
if err != nil {
|
timeSec, timeNsec int64
|
||||||
return nil, fmt.Errorf("failed to parse old time: %s", err)
|
// used to check for trailing characters
|
||||||
|
trailing rune
|
||||||
|
)
|
||||||
|
val := timeStateT.Old.Value()
|
||||||
|
n, _ := fmt.Sscanf(val, "%d.%d%c", &timeSec, &timeNsec, &trailing)
|
||||||
|
if n != 2 {
|
||||||
|
return nil, fmt.Errorf("failed to parse old time: invalid format %q", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTime := new(KeyVal)
|
newTime := new(KeyVal)
|
||||||
*newTime = KeyVal(fmt.Sprintf("tar_time=%d.000000000", int64(time)))
|
*newTime = KeyVal(fmt.Sprintf("tar_time=%d.%9.9d", timeSec, 0))
|
||||||
|
|
||||||
diffs["tar_time"].Old = newTime
|
diffs["tar_time"].Old = newTime
|
||||||
} else if diffs["tar_time"].New == nil {
|
} else if diffs["tar_time"].New == nil {
|
||||||
time, err := strconv.ParseFloat(timeStateT.New.Value(), 64)
|
var (
|
||||||
if err != nil {
|
timeSec, timeNsec int64
|
||||||
return nil, fmt.Errorf("failed to parse new time: %s", err)
|
// used to check for trailing characters
|
||||||
|
trailing rune
|
||||||
|
)
|
||||||
|
val := timeStateT.New.Value()
|
||||||
|
n, _ := fmt.Sscanf(val, "%d.%d%c", &timeSec, &timeNsec, &trailing)
|
||||||
|
if n != 2 {
|
||||||
|
return nil, fmt.Errorf("failed to parse new time: invalid format %q", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTime := new(KeyVal)
|
newTime := new(KeyVal)
|
||||||
*newTime = KeyVal(fmt.Sprintf("tar_time=%d.000000000", int64(time)))
|
*newTime = KeyVal(fmt.Sprintf("tar_time=%d.%9.9d", timeSec, 0))
|
||||||
|
|
||||||
diffs["tar_time"].New = newTime
|
diffs["tar_time"].New = newTime
|
||||||
} else {
|
} else {
|
||||||
|
|
96
test/cli/0012trunc.sh
Normal file
96
test/cli/0012trunc.sh
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
name=$(basename $0)
|
||||||
|
root="$(dirname $(dirname $(dirname $0)))"
|
||||||
|
gomtree=$(go run ${root}/test/realpath/main.go ${root}/gomtree)
|
||||||
|
t=$(mktemp -d /tmp/go-mtree.XXXXXX)
|
||||||
|
|
||||||
|
echo "[${name}] Running in ${t}"
|
||||||
|
|
||||||
|
## Make sure that comparing manifests with tar_time and time are correctly
|
||||||
|
## truncated.
|
||||||
|
|
||||||
|
pushd ${root}
|
||||||
|
mkdir -p ${t}/root
|
||||||
|
|
||||||
|
date="2025-09-05T13:05:10" # POSIX format for "touch -d".
|
||||||
|
|
||||||
|
echo "less than .5" >${t}/root/lowerhalf
|
||||||
|
touch -d "$date.1000" ${t}/root/lowerhalf
|
||||||
|
echo "more than .5" >${t}/root/upperhalf
|
||||||
|
touch -d "$date.8000" ${t}/root/upperhalf
|
||||||
|
echo "no subsecond" >${t}/root/tartime
|
||||||
|
touch -d "$date.0000" ${t}/root/tartime
|
||||||
|
|
||||||
|
keywords=type,uid,gid,nlink,link,mode,flags,xattr,size,sha256
|
||||||
|
|
||||||
|
# Generate regular manifests with time and tar_time.
|
||||||
|
${gomtree} -c -k ${keywords},time -p ${t}/root -f ${t}/time.mtree
|
||||||
|
${gomtree} -c -k ${keywords},tar_time -p ${t}/root -f ${t}/tartime.mtree
|
||||||
|
|
||||||
|
# Make sure that tar_time truncates the value.
|
||||||
|
unix="$(date -d ${date} +%s)"
|
||||||
|
grep -q "lowerhalf.*tar_time=$unix.000000000" ${t}/tartime.mtree
|
||||||
|
grep -q "upperhalf.*tar_time=$unix.000000000" ${t}/tartime.mtree
|
||||||
|
grep -q "tartime.*tar_time=$unix.000000000" ${t}/tartime.mtree
|
||||||
|
|
||||||
|
# Validation with both manifests should still succeed.
|
||||||
|
${gomtree} validate -p ${t}/root -f ${t}/time.mtree
|
||||||
|
${gomtree} validate -p ${t}/root -f ${t}/tartime.mtree
|
||||||
|
# Manifest comparison should also succeed.
|
||||||
|
${gomtree} validate -f ${t}/tartime.mtree -f ${t}/time.mtree
|
||||||
|
${gomtree} validate -f ${t}/time.mtree -f ${t}/tartime.mtree
|
||||||
|
|
||||||
|
# Truncate the on-disk timestamps manually.
|
||||||
|
touch -d "$date.0000" ${t}/root/lowerhalf
|
||||||
|
touch -d "$date.0000" ${t}/root/upperhalf
|
||||||
|
touch -d "$date.0000" ${t}/root/tartime
|
||||||
|
|
||||||
|
# Only the tar_time manifest should succeed.
|
||||||
|
(! ${gomtree} validate -p ${t}/root -f ${t}/time.mtree)
|
||||||
|
${gomtree} validate -p ${t}/root -f ${t}/tartime.mtree
|
||||||
|
${gomtree} validate -k ${keywords},time -p ${t}/root -f ${t}/tartime.mtree
|
||||||
|
# ... unless you force the usage of tar_time.
|
||||||
|
${gomtree} validate -k ${keywords},tar_time -p ${t}/root -f ${t}/time.mtree
|
||||||
|
|
||||||
|
# The same goes for if you generate the manifests and compare them instead.
|
||||||
|
${gomtree} -c -k ${keywords},time -p ${t}/root -f ${t}/time-trunc.mtree
|
||||||
|
${gomtree} -c -k ${keywords},tar_time -p ${t}/root -f ${t}/tartime-trunc.mtree
|
||||||
|
# Comparing time with time should fail ...
|
||||||
|
(! ${gomtree} validate -f ${t}/time.mtree -f ${t}/time-trunc.mtree)
|
||||||
|
(! ${gomtree} validate -f ${t}/time-trunc.mtree -f ${t}/time.mtree)
|
||||||
|
# ... tar_time with tar_time should succeed ...
|
||||||
|
${gomtree} validate -f ${t}/tartime.mtree -f ${t}/tartime-trunc.mtree
|
||||||
|
${gomtree} validate -f ${t}/tartime-trunc.mtree -f ${t}/tartime.mtree
|
||||||
|
# ... old tar_time with new time should succeed ...
|
||||||
|
${gomtree} validate -f ${t}/tartime.mtree -f ${t}/time-trunc.mtree
|
||||||
|
${gomtree} validate -f ${t}/time-trunc.mtree -f ${t}/tartime.mtree
|
||||||
|
# ... and new tar_time against old time should succeed.
|
||||||
|
${gomtree} validate -f ${t}/tartime-trunc.mtree -f ${t}/time.mtree
|
||||||
|
${gomtree} validate -f ${t}/time.mtree -f ${t}/tartime-trunc.mtree
|
||||||
|
|
||||||
|
# Change the timestamp entirely.
|
||||||
|
touch -d "1997-03-25T13:40:00" ${t}/root/lowerhalf
|
||||||
|
touch -d "1997-03-25T13:40:00" ${t}/root/upperhalf
|
||||||
|
touch -d "1997-03-25T13:40:00" ${t}/root/tartime
|
||||||
|
|
||||||
|
# Now all validations should fail.
|
||||||
|
(! ${gomtree} validate -p ${t}/root -f ${t}/time.mtree)
|
||||||
|
(! ${gomtree} validate -p ${t}/root -f ${t}/tartime.mtree)
|
||||||
|
(! ${gomtree} validate -k ${keywords},tar_time -p ${t}/root -f ${t}/time.mtree)
|
||||||
|
(! ${gomtree} validate -k ${keywords},time -p ${t}/root -f ${t}/tartime.mtree)
|
||||||
|
|
||||||
|
# Ditto for generating the manifests and comparing them.
|
||||||
|
${gomtree} -c -k ${keywords},time -p ${t}/root -f ${t}/time-change.mtree
|
||||||
|
${gomtree} -c -k ${keywords},tar_time -p ${t}/root -f ${t}/tartime-change.mtree
|
||||||
|
|
||||||
|
# Try all combinations.
|
||||||
|
lefts=( ${t}/{tar,}time{,-trunc}.mtree )
|
||||||
|
rights=( ${t}/{tar,}time-change.mtree )
|
||||||
|
for left in "${lefts[@]}"; do
|
||||||
|
for right in "${rights[@]}"; do
|
||||||
|
(! ${gomtree} validate -f ${left} -f ${right})
|
||||||
|
(! ${gomtree} validate -f ${right} -f ${left})
|
||||||
|
done
|
||||||
|
done
|
Loading…
Add table
Add a link
Reference in a new issue