11package configs
22
33import (
4+ "errors"
45 "fmt"
56 "os"
7+ "strconv"
8+
9+ "golang.org/x/sys/unix"
610)
711
812const (
@@ -12,21 +16,11 @@ const (
1216// TODO Windows: This can be factored out in the future
1317
1418type Device struct {
15- // Device type, block, char, etc.
16- Type rune `json:"type"`
19+ DeviceRule
1720
1821 // Path to the device.
1922 Path string `json:"path"`
2023
21- // Major is the device's major number.
22- Major int64 `json:"major"`
23-
24- // Minor is the device's minor number.
25- Minor int64 `json:"minor"`
26-
27- // Cgroup permissions format, rwm.
28- Permissions string `json:"permissions"`
29-
3024 // FileMode permission bits for the device.
3125 FileMode os.FileMode `json:"file_mode"`
3226
@@ -35,23 +29,154 @@ type Device struct {
3529
3630 // Gid of the device.
3731 Gid uint32 `json:"gid"`
32+ }
3833
39- // Write the file to the allowed list
40- Allow bool `json:"allow"`
34+ // DevicePermissions is a cgroupv1-style string to represent device access. It
35+ // has to be a string for backward compatibility reasons, hence why it has
36+ // methods to do set operations.
37+ type DevicePermissions string
38+
39+ const (
40+ deviceRead uint = (1 << iota )
41+ deviceWrite
42+ deviceMknod
43+ )
44+
45+ func (p DevicePermissions ) toSet () uint {
46+ var set uint
47+ for _ , perm := range p {
48+ switch perm {
49+ case 'r' :
50+ set |= deviceRead
51+ case 'w' :
52+ set |= deviceWrite
53+ case 'm' :
54+ set |= deviceMknod
55+ }
56+ }
57+ return set
58+ }
59+
60+ func fromSet (set uint ) DevicePermissions {
61+ var perm string
62+ if set & deviceRead == deviceRead {
63+ perm += "r"
64+ }
65+ if set & deviceWrite == deviceWrite {
66+ perm += "w"
67+ }
68+ if set & deviceMknod == deviceMknod {
69+ perm += "m"
70+ }
71+ return DevicePermissions (perm )
72+ }
73+
74+ // Union returns the union of the two sets of DevicePermissions.
75+ func (p DevicePermissions ) Union (o DevicePermissions ) DevicePermissions {
76+ lhs := p .toSet ()
77+ rhs := o .toSet ()
78+ return fromSet (lhs | rhs )
79+ }
80+
81+ // Difference returns the set difference of the two sets of DevicePermissions.
82+ // In set notation, A.Difference(B) gives you A\B.
83+ func (p DevicePermissions ) Difference (o DevicePermissions ) DevicePermissions {
84+ lhs := p .toSet ()
85+ rhs := o .toSet ()
86+ return fromSet (lhs &^ rhs )
87+ }
88+
89+ // Intersection computes the intersection of the two sets of DevicePermissions.
90+ func (p DevicePermissions ) Intersection (o DevicePermissions ) DevicePermissions {
91+ lhs := p .toSet ()
92+ rhs := o .toSet ()
93+ return fromSet (lhs & rhs )
94+ }
95+
96+ // IsEmpty returns whether the set of permissions in a DevicePermissions is
97+ // empty.
98+ func (p DevicePermissions ) IsEmpty () bool {
99+ return p == DevicePermissions ("" )
100+ }
101+
102+ // IsValid returns whether the set of permissions is a subset of valid
103+ // permissions (namely, {r,w,m}).
104+ func (p DevicePermissions ) IsValid () bool {
105+ return p == fromSet (p .toSet ())
106+ }
107+
108+ type DeviceType rune
109+
110+ const (
111+ WildcardDevice DeviceType = 'a'
112+ BlockDevice DeviceType = 'b'
113+ CharDevice DeviceType = 'c' // or 'u'
114+ FifoDevice DeviceType = 'p'
115+ )
116+
117+ func (t DeviceType ) IsValid () bool {
118+ switch t {
119+ case WildcardDevice , BlockDevice , CharDevice , FifoDevice :
120+ return true
121+ default :
122+ return false
123+ }
41124}
42125
43- func (d * Device ) CgroupString () string {
44- return fmt .Sprintf ("%c %s:%s %s" , d .Type , deviceNumberString (d .Major ), deviceNumberString (d .Minor ), d .Permissions )
126+ func (t DeviceType ) CanMknod () bool {
127+ switch t {
128+ case BlockDevice , CharDevice , FifoDevice :
129+ return true
130+ default :
131+ return false
132+ }
133+ }
134+
135+ func (t DeviceType ) CanCgroup () bool {
136+ switch t {
137+ case WildcardDevice , BlockDevice , CharDevice :
138+ return true
139+ default :
140+ return false
141+ }
45142}
46143
47- func (d * Device ) Mkdev () int {
48- return int ((d .Major << 8 ) | (d .Minor & 0xff ) | ((d .Minor & 0xfff00 ) << 12 ))
144+ type DeviceRule struct {
145+ // Type of device ('c' for char, 'b' for block). If set to 'a', this rule
146+ // acts as a wildcard and all fields other than Allow are ignored.
147+ Type DeviceType `json:"type"`
148+
149+ // Major is the device's major number.
150+ Major int64 `json:"major"`
151+
152+ // Minor is the device's minor number.
153+ Minor int64 `json:"minor"`
154+
155+ // Permissions is the set of permissions that this rule applies to (in the
156+ // cgroupv1 format -- any combination of "rwm").
157+ Permissions DevicePermissions `json:"permissions"`
158+
159+ // Allow specifies whether this rule is allowed.
160+ Allow bool `json:"allow"`
161+ }
162+
163+ func (d * DeviceRule ) CgroupString () string {
164+ var (
165+ major = strconv .FormatInt (d .Major , 10 )
166+ minor = strconv .FormatInt (d .Minor , 10 )
167+ )
168+ if d .Major == Wildcard {
169+ major = "*"
170+ }
171+ if d .Minor == Wildcard {
172+ minor = "*"
173+ }
174+ return fmt .Sprintf ("%c %s:%s %s" , d .Type , major , minor , d .Permissions )
49175}
50176
51- // deviceNumberString converts the device number to a string return result.
52- func deviceNumberString (number int64 ) string {
53- if number == Wildcard {
54- return "*"
177+ func (d * DeviceRule ) Mkdev () (uint64 , error ) {
178+ if d .Major == Wildcard || d .Minor == Wildcard {
179+ return 0 , errors .New ("cannot mkdev() device with wildcards" )
55180 }
56- return fmt . Sprint ( number )
181+ return unix . Mkdev ( uint32 ( d . Major ), uint32 ( d . Minor )), nil
57182}
0 commit comments