summaryrefslogtreecommitdiff
path: root/lib/btrfs
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-13 21:44:18 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-13 21:44:18 -0600
commit296e9fc0f8812ce0c5684ff99f84e80eef07cd4c (patch)
treef2c0219b5a5db0603af6c55acb6f1684742989fd /lib/btrfs
parent436e1681c9fcda246c6d84526fc79c87adc7b28d (diff)
Move files to different packages [ci-skip]
Diffstat (limited to 'lib/btrfs')
-rw-r--r--lib/btrfs/btrfsitem/statmode.go100
-rw-r--r--lib/btrfs/internal/uuid.go76
-rw-r--r--lib/btrfs/internal/uuid_test.go67
3 files changed, 243 insertions, 0 deletions
diff --git a/lib/btrfs/btrfsitem/statmode.go b/lib/btrfs/btrfsitem/statmode.go
new file mode 100644
index 0000000..2cca56d
--- /dev/null
+++ b/lib/btrfs/btrfsitem/statmode.go
@@ -0,0 +1,100 @@
+// Copyright (C) 2020-2021 Ambassador Labs
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Based on https://github.com/datawire/ocibuild/blob/master/pkg/python/stat.go
+
+package linux
+
+type StatMode uint32
+
+//nolint:deadcode,varcheck // not all of these modes will be used
+const (
+ // 16 bits = 5⅓ octal characters
+
+ ModeFmt StatMode = 0o17_0000 // mask for the type bits
+
+ _ModeFmtUnused000 StatMode = 0o00_0000
+ ModeFmtNamedPipe StatMode = 0o01_0000 // type: named pipe (FIFO)
+ ModeFmtCharDevice StatMode = 0o02_0000 // type: character device
+ _ModeFmtUnused003 StatMode = 0o03_0000
+ ModeFmtDir StatMode = 0o04_0000 // type: directory
+ _ModeFmtUnused005 StatMode = 0o05_0000
+ ModeFmtBlockDevice StatMode = 0o06_0000 // type: block device
+ _ModeFmtUnused007 StatMode = 0o07_0000
+ ModeFmtRegular StatMode = 0o10_0000 // type: regular file
+ _ModeFmtUnused011 StatMode = 0o11_0000
+ ModeFmtSymlink StatMode = 0o12_0000 // type: symbolic link
+ _ModeFmtUnused013 StatMode = 0o13_0000
+ ModeFmtSocket StatMode = 0o14_0000 // type: socket file
+ _ModeFmtUnused015 StatMode = 0o15_0000
+ _ModeFmtUnused016 StatMode = 0o16_0000
+ _ModeFmtUnused017 StatMode = 0o17_0000
+
+ ModePerm StatMode = 0o00_7777 // mask for permission bits
+
+ ModePermSetUID StatMode = 0o00_4000 // permission: set user id
+ ModePermSetGID StatMode = 0o00_2000 // permission: set group ID
+ ModePermSticky StatMode = 0o00_1000 // permission: sticky bit
+
+ ModePermUsrR StatMode = 0o00_0400 // permission: user: read
+ ModePermUsrW StatMode = 0o00_0200 // permission: user: write
+ ModePermUsrX StatMode = 0o00_0100 // permission: user: execute
+
+ ModePermGrpR StatMode = 0o00_0040 // permission: group: read
+ ModePermGrpW StatMode = 0o00_0020 // permission: group: write
+ ModePermGrpX StatMode = 0o00_0010 // permission: group: execute
+
+ ModePermOthR StatMode = 0o00_0004 // permission: other: read
+ ModePermOthW StatMode = 0o00_0002 // permission: other: write
+ ModePermOthX StatMode = 0o00_0001 // permission: other: execute
+)
+
+// IsDir reports whether mode describes a directory.
+//
+// That is, it tests that the ModeFmt bits are set to ModeFmtDir.
+func (mode StatMode) IsDir() bool {
+ return mode&ModeFmt == ModeFmtDir
+}
+
+// IsRegular reports whether m describes a regular file.
+//
+// That is, it tests that the ModeFmt bits are set to ModeFmtRegular.
+func (mode StatMode) IsRegular() bool {
+ return mode&ModeFmt == ModeFmtRegular
+}
+
+// String returns a textual representation of the mode.
+//
+// This is the format that POSIX specifies for showing the mode in the
+// output of the `ls -l` command. POSIX does not specify the
+// character to use to indicate a ModeFmtSocket file; this method uses
+// 's' (GNU `ls` behavior; though POSIX notes that many
+// implementations use '=' for sockets).
+func (mode StatMode) String() string {
+ buf := [10]byte{
+ // type: This string is easy; it directly pairs with
+ // the above ModeFmtXXX list above; the character in
+ // the string left-to-right corresponds with the
+ // constant in the list top-to-bottom.
+ "?pc?d?b?-?l?s???"[mode>>12],
+
+ // owner
+ "-r"[(mode>>8)&0o1],
+ "-w"[(mode>>7)&0o1],
+ "-xSs"[((mode>>6)&0o1)|((mode>>10)&0o2)],
+
+ // group
+ "-r"[(mode>>5)&0o1],
+ "-w"[(mode>>4)&0o1],
+ "-xSs"[((mode>>3)&0o1)|((mode>>9)&0o2)],
+
+ // group
+ "-r"[(mode>>2)&0o1],
+ "-w"[(mode>>1)&0o1],
+ "-xTt"[((mode>>0)&0o1)|((mode>>8)&0o2)],
+ }
+
+ return string(buf[:])
+}
diff --git a/lib/btrfs/internal/uuid.go b/lib/btrfs/internal/uuid.go
new file mode 100644
index 0000000..a16d10f
--- /dev/null
+++ b/lib/btrfs/internal/uuid.go
@@ -0,0 +1,76 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package util
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+)
+
+type UUID [16]byte
+
+func (uuid UUID) String() string {
+ str := hex.EncodeToString(uuid[:])
+ return strings.Join([]string{
+ str[:8],
+ str[8:12],
+ str[12:16],
+ str[16:20],
+ str[20:32],
+ }, "-")
+}
+
+func (a UUID) Cmp(b UUID) int {
+ for i := range a {
+ if d := int(a[i]) - int(b[i]); d != 0 {
+ return d
+ }
+ }
+ return 0
+}
+
+func (uuid UUID) Format(f fmt.State, verb rune) {
+ FormatByteArrayStringer(uuid, uuid[:], f, verb)
+}
+
+func ParseUUID(str string) (UUID, error) {
+ var ret UUID
+ j := 0
+ for i := 0; i < len(str); i++ {
+ if j >= len(ret)*2 {
+ return UUID{}, fmt.Errorf("too long to be a UUID: %q|%q", str[:i], str[i:])
+ }
+ c := str[i]
+ var v byte
+ switch {
+ case '0' <= c && c <= '9':
+ v = c - '0'
+ case 'a' <= c && c <= 'f':
+ v = c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ v = c - 'A' + 10
+ case c == '-':
+ continue
+ default:
+ return UUID{}, fmt.Errorf("illegal byte in UUID: %q|%q|%q", str[:i], str[i:i+1], str[i+1:])
+ }
+ if j%2 == 0 {
+ ret[j/2] = v << 4
+ } else {
+ ret[j/2] = (ret[j/2] & 0xf0) | (v & 0x0f)
+ }
+ j++
+ }
+ return ret, nil
+}
+
+func MustParseUUID(str string) UUID {
+ ret, err := ParseUUID(str)
+ if err != nil {
+ panic(err)
+ }
+ return ret
+}
diff --git a/lib/btrfs/internal/uuid_test.go b/lib/btrfs/internal/uuid_test.go
new file mode 100644
index 0000000..747ff6b
--- /dev/null
+++ b/lib/btrfs/internal/uuid_test.go
@@ -0,0 +1,67 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package util_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "git.lukeshu.com/btrfs-progs-ng/lib/util"
+)
+
+func TestParseUUID(t *testing.T) {
+ t.Parallel()
+ type TestCase struct {
+ Input string
+ OutputVal util.UUID
+ OutputErr string
+ }
+ testcases := map[string]TestCase{
+ "basic": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43", OutputVal: util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0x0c, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}},
+ "too-long": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43a", OutputErr: `too long to be a UUID: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"|"a"`},
+ "bad char": {Input: "a0dd94ej-e60c-42e8-8632-64e8d4765a43a", OutputErr: `illegal byte in UUID: "a0dd94e"|"j"|"-e60c-42e8-8632-64e8d4765a43a"`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ val, err := util.ParseUUID(tc.Input)
+ assert.Equal(t, tc.OutputVal, val)
+ if tc.OutputErr == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.OutputErr)
+ }
+ })
+ }
+}
+
+func TestUUIDFormat(t *testing.T) {
+ t.Parallel()
+ type TestCase struct {
+ InputUUID util.UUID
+ InputFmt string
+ Output string
+ }
+ uuid := util.MustParseUUID("a0dd94ed-e60c-42e8-8632-64e8d4765a43")
+ testcases := map[string]TestCase{
+ "s": {InputUUID: uuid, InputFmt: "%s", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "x": {InputUUID: uuid, InputFmt: "%x", Output: "a0dd94ede60c42e8863264e8d4765a43"},
+ "X": {InputUUID: uuid, InputFmt: "%X", Output: "A0DD94EDE60C42E8863264E8D4765A43"},
+ "v": {InputUUID: uuid, InputFmt: "%v", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "40s": {InputUUID: uuid, InputFmt: "|% 40s", Output: "| a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "#115v": {InputUUID: uuid, InputFmt: "|%#115v", Output: "| util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0xc, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}"},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ actual := fmt.Sprintf(tc.InputFmt, tc.InputUUID)
+ assert.Equal(t, tc.Output, actual)
+ })
+ }
+}