summaryrefslogtreecommitdiff
diff options
authorSamuele Pedroni <pedronis@lucediurna.net>2021-10-19 10:33:43 +0200
committerGitHub <noreply@github.com>2021-10-19 10:33:43 +0200
commitda507a2469d9f87ecb3fab753b17ccd49c77e7be (patch)
tree3810f9f61d7edd2bdb56b60243d0c4a42849667a
parentb512d2e442c4e190dc578c6ecbbb18e09e86db77 (diff)
parentbd7d85de123f0cc0fb467c57010189e14644fd2f (diff)
osutil/disks: fix bug in BlkIDEncodeLabel, add BlkIDDecodeLabel
Merge pull request #10916 from anonymouse64/feature/uc20-multi-volume-gadget-asset-updates-14 The existing code was not properly encoding non-allowed single length runes less than 0x10 by not including the prefixing "0". BlkIDDecodeLabel is needed since some labels we read from udev need to be compared with normal Go string values we get from i.e. gadget.yaml.
-rw-r--r--osutil/disks/labels.go94
-rw-r--r--osutil/disks/labels_test.go70
2 files changed, 160 insertions, 4 deletions
diff --git a/osutil/disks/labels.go b/osutil/disks/labels.go
index ab5c25245e..19aba4fc1d 100644
--- a/osutil/disks/labels.go
+++ b/osutil/disks/labels.go
@@ -22,6 +22,7 @@ package disks
import (
"bytes"
"fmt"
+ "strconv"
"strings"
"unicode/utf8"
)
@@ -39,10 +40,101 @@ func BlkIDEncodeLabel(in string) string {
case utf8.RuneLen(r) > 1:
buf.WriteRune(r)
case !strings.ContainsRune(allowed, r):
- fmt.Fprintf(buf, `\x%x`, r)
+ fmt.Fprintf(buf, `\x%02x`, r)
default:
buf.WriteRune(r)
}
}
return buf.String()
}
+
+type blkIdDecodeState int
+
+const (
+ stNormal blkIdDecodeState = iota
+ stSlashEscape
+ stSlashEscapeX
+ stSlashEscapeXNum
+)
+
+// BlkIDDecodeLabel decodes a string such as a filesystem or partition label
+// encoded by udev in BlkIDEncodeLabel for normal comparison, i.e.
+// "BIOS\x20Boot" becomes "BIOS Boot"
+func BlkIDDecodeLabel(in string) (string, error) {
+
+ const errFmtStr = "string is malformed, unexpected character '%c' not part of a valid escape sequence"
+
+ out := strings.Builder{}
+ escapedHexDigits := [2]rune{}
+ st := stNormal
+ for _, r := range in {
+ switch st {
+ case stNormal:
+ // check if this char is the beginning of an escape sequence
+ if r == '\\' {
+ st = stSlashEscape
+ continue
+ }
+ // otherwise write it
+ out.WriteRune(r)
+ case stSlashEscape:
+ // next char to check is 'x'
+ if r == 'x' {
+ st = stSlashEscapeX
+ continue
+ }
+ // otherwise it's a format error, "\" is not in the set of
+ // characters allowed, so if we see one that is not followed by an
+ // x, then the string is malformed and can't be decoded
+ return "", fmt.Errorf(errFmtStr, '\\')
+ case stSlashEscapeX:
+ // now we expect exactly two hex digits, since the encoding would
+ // have written valid multi-byte runes that are UTF8 directly
+ // without escaping, the only possible escaped runes are those which
+ // are one byte and not in the allowed set
+
+ // TODO: though can one have multi-byte runes that are not UTF8
+ // encodable? it seems the only possibilities are runes that are
+ // either in the surrogate range or that are larger than the maximum
+ // rune value - for now we will just ignore those
+
+ if strings.ContainsRune(`0123456789abcedf`, r) {
+ escapedHexDigits[0] = r
+ st = stSlashEscapeXNum
+ continue
+ }
+ return "", fmt.Errorf(errFmtStr, r)
+ case stSlashEscapeXNum:
+ // got one digit, make sure we get a second digit
+ if strings.ContainsRune(`0123456789abcedf`, r) {
+ escapedHexDigits[1] = r
+
+ // the escapedHexDigits can now be decoded and written out
+ v, err := strconv.ParseUint(string(escapedHexDigits[:]), 16, 8)
+ if err != nil {
+ // should be logically impossible, we ensured that only
+ // rune digits in the hexadecimal range above were put into this rune
+ // buffer
+ return "", fmt.Errorf("internal error, unable to parse escape sequence: %v", err)
+ }
+ escapedHexDigits = [2]rune{0, 0}
+ out.WriteRune(rune(v))
+ st = stNormal
+ continue
+ }
+ return "", fmt.Errorf(errFmtStr, r)
+ default:
+ return "", fmt.Errorf("internal error, unexpected parsing state")
+ }
+ }
+
+ // check that we had a valid end state
+ switch st {
+ case stNormal:
+ return out.String(), nil
+ case stSlashEscape, stSlashEscapeX, stSlashEscapeXNum:
+ return "", fmt.Errorf("string is malformed, unfinished escape sequence")
+ default:
+ return "", fmt.Errorf("internal error, unexpected parsing state")
+ }
+}
diff --git a/osutil/disks/labels_test.go b/osutil/disks/labels_test.go
index bc89b067e8..10277d8c54 100644
--- a/osutil/disks/labels_test.go
+++ b/osutil/disks/labels_test.go
@@ -33,7 +33,7 @@ type diskLabelSuite struct{}
var _ = Suite(&diskLabelSuite{})
-func (ts *diskLabelSuite) TestEncodeHexBlkIDFormat(c *C) {
+func (ts *diskLabelSuite) TestBlkIDEncodeDecodeLabelHappy(c *C) {
// Test output obtained with the following program:
//
// #include <string.h>
@@ -72,8 +72,9 @@ func (ts *diskLabelSuite) TestEncodeHexBlkIDFormat(c *C) {
// these are "unsafe" chars, so they get encoded
{"ubuntu data", `ubuntu\x20data`},
- {"ubuntu\ttab", `ubuntu\x9tab`},
- {"ubuntu\nnewline", `ubuntu\xanewline`},
+ {"ubuntu\ttab", `ubuntu\x09tab`},
+ {"ubuntu\t9tab", `ubuntu\x099tab`},
+ {"ubuntu\nnewline", `ubuntu\x0anewline`},
{"foo bar", `foo\x20bar`},
{"foo/bar", `foo\x2fbar`},
{"foo/../bar", `foo\x2f..\x2fbar`},
@@ -81,9 +82,72 @@ func (ts *diskLabelSuite) TestEncodeHexBlkIDFormat(c *C) {
{"pinkiƩ pie", `pinkiƩ\x20pie`},
{"(EFI Boot)", `\x28EFI\x20Boot\x29`},
{"[System Boot]", `\x5bSystem\x20Boot\x5d`},
+ // 0x7e is just a 1-rune long character that is not in the allowed set
+ // to demonstrate that these two input strings are encoded/decoded
+ // properly with the constant double width
+ {"ubuntu\x7etab", `ubuntu\x7etab`},
+ {"ubuntu\x07" + "etab", `ubuntu\x07etab`},
+ // works when the only character is an escaped one too
+ {"\t", `\x09`},
}
for _, t := range tt {
c.Logf("tc: %v %q", t.in, t.out)
c.Assert(disks.BlkIDEncodeLabel(t.in), Equals, t.out)
+
+ // make sure the other way around works too
+ expin, err := disks.BlkIDDecodeLabel(t.out)
+ c.Assert(err, IsNil)
+
+ c.Assert(expin, Equals, t.in)
+ }
+}
+
+func (ts *diskLabelSuite) TestBlkIDDecodeLabelUnhappy(c *C) {
+ tt := []struct {
+ in string
+ experr string
+ }{
+ {
+ `\x7z`,
+ "string is malformed, unexpected character 'z' not part of a valid escape sequence",
+ },
+ {
+ `\x09\x7y`,
+ "string is malformed, unexpected character 'y' not part of a valid escape sequence",
+ },
+ {
+ `\z`,
+ `string is malformed, unexpected character '\\' not part of a valid escape sequence`,
+ },
+ {
+ `\`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ {
+ `\x40\`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ {
+ `\x`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ {
+ `\x40\x`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ {
+ `\x0`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ {
+ `\x40\x4`,
+ `string is malformed, unfinished escape sequence`,
+ },
+ }
+
+ for _, t := range tt {
+ c.Logf("input: %q", t.in)
+ _, err := disks.BlkIDDecodeLabel(t.in)
+ c.Assert(err, ErrorMatches, t.experr)
}
}