Skip to content

Commit 30302a2

Browse files
committed
mount: add string representation of mount flags
When reading mount errors, it is quite hard to make sense of mount flags in their hex form. As this is the error path, the minor performance impact of constructing a string is probably not worth hyper-optimising. Signed-off-by: Aleksa Sarai <[email protected]>
1 parent eeae96b commit 30302a2

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

libcontainer/mount_linux.go

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io/fs"
77
"os"
88
"strconv"
9+
"strings"
910

1011
"github.com/sirupsen/logrus"
1112
"golang.org/x/sys/unix"
@@ -45,6 +46,64 @@ type mountError struct {
4546
err error
4647
}
4748

49+
// int32plus is a collection of int types with >=32 bits.
50+
type int32plus interface {
51+
int | uint | int32 | uint32 | int64 | uint64 | uintptr
52+
}
53+
54+
// stringifyMountFlags converts mount(2) flags to a string that you can use in
55+
// error messages.
56+
func stringifyMountFlags[Int int32plus](flags Int) string {
57+
flagNames := []struct {
58+
name string
59+
bits Int
60+
}{
61+
{"MS_RDONLY", unix.MS_RDONLY},
62+
{"MS_NOSUID", unix.MS_NOSUID},
63+
{"MS_NODEV", unix.MS_NODEV},
64+
{"MS_NOEXEC", unix.MS_NOEXEC},
65+
{"MS_SYNCHRONOUS", unix.MS_SYNCHRONOUS},
66+
{"MS_REMOUNT", unix.MS_REMOUNT},
67+
{"MS_MANDLOCK", unix.MS_MANDLOCK},
68+
{"MS_DIRSYNC", unix.MS_DIRSYNC},
69+
{"MS_NOSYMFOLLOW", unix.MS_NOSYMFOLLOW},
70+
// No (1 << 9) flag.
71+
{"MS_NOATIME", unix.MS_NOATIME},
72+
{"MS_NODIRATIME", unix.MS_NODIRATIME},
73+
{"MS_BIND", unix.MS_BIND},
74+
{"MS_MOVE", unix.MS_MOVE},
75+
{"MS_REC", unix.MS_REC},
76+
// MS_VERBOSE was deprecated and swapped to MS_SILENT.
77+
{"MS_SILENT", unix.MS_SILENT},
78+
{"MS_POSIXACL", unix.MS_POSIXACL},
79+
{"MS_UNBINDABLE", unix.MS_UNBINDABLE},
80+
{"MS_PRIVATE", unix.MS_PRIVATE},
81+
{"MS_SLAVE", unix.MS_SLAVE},
82+
{"MS_SHARED", unix.MS_SHARED},
83+
{"MS_RELATIME", unix.MS_RELATIME},
84+
// MS_KERNMOUNT (1 << 22) is internal to the kernel.
85+
{"MS_I_VERSION", unix.MS_I_VERSION},
86+
{"MS_STRICTATIME", unix.MS_STRICTATIME},
87+
{"MS_LAZYTIME", unix.MS_LAZYTIME},
88+
}
89+
var (
90+
flagSet []string
91+
seenBits Int
92+
)
93+
for _, flag := range flagNames {
94+
if flags&flag.bits == flag.bits {
95+
seenBits |= flag.bits
96+
flagSet = append(flagSet, flag.name)
97+
}
98+
}
99+
// If there were any remaining flags specified we don't know the name of,
100+
// just add them in an 0x... format.
101+
if remaining := flags &^ seenBits; remaining != 0 {
102+
flagSet = append(flagSet, "0x"+strconv.FormatUint(uint64(remaining), 16))
103+
}
104+
return strings.Join(flagSet, "|")
105+
}
106+
48107
// Error provides a string error representation.
49108
func (e *mountError) Error() string {
50109
out := e.op + " "
@@ -62,7 +121,7 @@ func (e *mountError) Error() string {
62121
}
63122

64123
if e.flags != uintptr(0) {
65-
out += ", flags=0x" + strconv.FormatUint(uint64(e.flags), 16)
124+
out += ", flags=" + stringifyMountFlags(e.flags)
66125
}
67126
if e.data != "" {
68127
out += ", data=" + e.data

libcontainer/mount_linux_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package libcontainer
2+
3+
import (
4+
"testing"
5+
6+
"golang.org/x/sys/unix"
7+
)
8+
9+
func TestStringifyMountFlags(t *testing.T) {
10+
for _, test := range []struct {
11+
name string
12+
flags uintptr
13+
expected string
14+
}{
15+
{"Empty", 0, ""},
16+
// Single valid flags.
17+
{"Single-MS_RDONLY", unix.MS_RDONLY, "MS_RDONLY"},
18+
{"Single-MS_NOSUID", unix.MS_NOSUID, "MS_NOSUID"},
19+
{"Single-MS_NODEV", unix.MS_NODEV, "MS_NODEV"},
20+
{"Single-MS_NOEXEC", unix.MS_NOEXEC, "MS_NOEXEC"},
21+
{"Single-MS_SYNCHRONOUS", unix.MS_SYNCHRONOUS, "MS_SYNCHRONOUS"},
22+
{"Single-MS_REMOUNT", unix.MS_REMOUNT, "MS_REMOUNT"},
23+
{"Single-MS_MANDLOCK", unix.MS_MANDLOCK, "MS_MANDLOCK"},
24+
{"Single-MS_DIRSYNC", unix.MS_DIRSYNC, "MS_DIRSYNC"},
25+
{"Single-MS_NOSYMFOLLOW", unix.MS_NOSYMFOLLOW, "MS_NOSYMFOLLOW"},
26+
{"Single-MS_NOATIME", unix.MS_NOATIME, "MS_NOATIME"},
27+
{"Single-MS_NODIRATIME", unix.MS_NODIRATIME, "MS_NODIRATIME"},
28+
{"Single-MS_BIND", unix.MS_BIND, "MS_BIND"},
29+
{"Single-MS_MOVE", unix.MS_MOVE, "MS_MOVE"},
30+
{"Single-MS_REC", unix.MS_REC, "MS_REC"},
31+
{"Single-MS_SILENT", unix.MS_SILENT, "MS_SILENT"},
32+
{"Single-MS_POSIXACL", unix.MS_POSIXACL, "MS_POSIXACL"},
33+
{"Single-MS_UNBINDABLE", unix.MS_UNBINDABLE, "MS_UNBINDABLE"},
34+
{"Single-MS_PRIVATE", unix.MS_PRIVATE, "MS_PRIVATE"},
35+
{"Single-MS_SLAVE", unix.MS_SLAVE, "MS_SLAVE"},
36+
{"Single-MS_SHARED", unix.MS_SHARED, "MS_SHARED"},
37+
{"Single-MS_RELATIME", unix.MS_RELATIME, "MS_RELATIME"},
38+
{"Single-MS_KERNMOUNT", unix.MS_KERNMOUNT, "0x400000"},
39+
{"Single-MS_I_VERSION", unix.MS_I_VERSION, "MS_I_VERSION"},
40+
{"Single-MS_STRICTATIME", unix.MS_STRICTATIME, "MS_STRICTATIME"},
41+
{"Single-MS_LAZYTIME", unix.MS_LAZYTIME, "MS_LAZYTIME"},
42+
// Invalid flag value.
43+
{"Unknown-512", 1 << 9, "0x200"},
44+
// Multiple flag values at the same time.
45+
{"Multiple-Valid1", unix.MS_RDONLY | unix.MS_REC | unix.MS_BIND, "MS_RDONLY|MS_BIND|MS_REC"},
46+
{"Multiple-Valid2", unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_REC | unix.MS_NODIRATIME | unix.MS_I_VERSION, "MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NODIRATIME|MS_REC|MS_I_VERSION"},
47+
{"Multiple-Mixed", unix.MS_REC | unix.MS_BIND | (1 << 9) | (1 << 31), "MS_BIND|MS_REC|0x80000200"},
48+
} {
49+
got := stringifyMountFlags(test.flags)
50+
if got != test.expected {
51+
t.Errorf("%s: stringifyMountFlags(0x%x) = %q, expected %q", test.name, test.flags, got, test.expected)
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)