fix(snapshots): Parse time of day using base 10 - #5583
Open
lsamaciel wants to merge 1 commit into
Open
Conversation
TimeOfDay.Parse used fmt.Sscanf with the %v verb, which detects the number base from the input, so an hour written with a leading zero was parsed as octal: "08:30" and "09:30" were rejected with "invalid time of day, must be HH:MM", while "00:30" through "07:30" happened to work. The minute went through the same base detection, but since it is the last verb in the format string, the leftover digit was silently discarded instead: "10:08" and "10:09" were accepted and stored as 10:00. Parse both halves with strconv.Atoi, which always uses base 10 and rejects trailing garbage, and assign to the receiver only once both halves are valid. The error messages now include the value that failed to parse, which also identifies the offending entry when a comma-separated list is passed to kopia policy set --snapshot-time. Fixes kopia#4325
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4325
Problem
kopia policy set <path> --snapshot-time=09:45fails:09:45isHH:MM, so the message points away from the actual problem. Only thehour field is affected, and only for
08and09:0:30,00:3008:3007:30,7:3009:308:30,9:3010:30,18:30The minute field has the same defect with a different symptom:
10:08and10:09are accepted and silently stored as10:00.Both hit the natural way of writing a morning schedule, which is when a lot of
people run backups. A comma-separated list is rejected as a whole without saying
which entry failed, so
--snapshot-time=3:45,09:45,15:45,21:45fails while--snapshot-time=3:45,9:45,15:45,21:45works.--snapshot-time-crontabis not affected.Root cause
TimeOfDay.Parseparsed both halves with a singlefmt.Sscanf:For integers the
%vverb callsscanBasePrefix()infmt/scan.go, whichtreats a leading
0as a base prefix and falls through to octal. So the hour08scans as the octal token0, the leftover8no longer matches the literal:in the format, andSscanffails.00–07are valid octal, which is whythey appear to work.
The minute is scanned the same way, but it is the last verb in the format
string, and
Sscanfignores trailing input — so the leftover digit is droppedand no error is reported. That is the silent
10:08->10:00.The
02in%02vis a width, not a base, so it never made the two halvesbehave differently.
Fix
Split on the colon and convert each half with
strconv.Atoi, which alwaysparses base 10 and rejects trailing garbage. The receiver is only written once
both halves are valid, so a failed parse no longer leaves a half-updated value
behind, and each error message now includes the offending input — which also
identifies the bad entry when a list is passed to
--snapshot-time.Why not just
%dChanging the verb from
%vto%dkeepsSscanfand fixes the reported bug:the base is then fixed at 10,
getBase()never callsscanBasePrefix(), andevery hour and minute parses correctly with or without a leading zero. I tested
that:
TestTimeOfDayParsepasses in full againstSscanfwith"%d:%d".It leaves three defects in place, though:
1:2:3-> 1:02,10:30abc-> 10:30,10:0x8-> 10:00;99:99returns anerror after storing
TimeOfDay{99, 99};comma-separated list still does not say which entry caused it.
Keeping the original width (
"%d:%02d") is worse still: the width truncates thefield, so
10:100parses as 10:10 and1:234as 01:23.Behavior changes beyond the fix
The new parser is stricter than
Sscanfwas. Input the old parser accepted, andwhat happens to it now:
10:0810:30:00,10:30abc0x10:001_0:3010: 30Surrounding whitespace is still tolerated (
--snapshot-time="8:00, 9:30"keepsworking). The stricter cases all look like input that was never intended to
parse; flagging it here in case it should be called out in the release notes.
Each row above has a case in
TestTimeOfDayParseInvalid, so the grammar cannotquietly loosen again.
TimeOfDay.String()is deliberately left alone: it renders the hour without aleading zero, so a policy set as
08:45is shown as8:45bykopia policy show. Both forms parse, so this round-trips; changing the outputformat felt out of scope for a bug fix.
Tests
TestTimeOfDayParsecovers all 24 hours and all 60 minutes, each with andwithout a leading zero, and asserts the stored value rather than only the
absence of an error — that is what catches the silent
10:08->10:00case.TestTimeOfDayParseInvalidcovers malformed and out-of-range input, and assertsthat the error names the input and that a failed parse leaves the receiver
untouched.
Against the previous implementation, 26 of the new subtests fail; all pass with
this change.
go vet,golangci-lint, andgo test ./snapshot/... ./cli/...are clean.