package devices import ( "fmt" "os" "strconv" ) const ( Wildcard = -1 ) type Device struct { Rule // Path to the device. Path string `json:"path"` // FileMode permission bits for the device. FileMode os.FileMode `json:"file_mode"` // Uid of the device. Uid uint32 `json:"uid"` // Gid of the device. Gid uint32 `json:"gid"` } // Permissions is a cgroupv1-style string to represent device access. It // has to be a string for backward compatibility reasons, hence why it has // methods to do set operations. type Permissions string const ( deviceRead uint = (1 << iota) deviceWrite deviceMknod ) func (p Permissions) toSet() uint { var set uint for _, perm := range p { switch perm { case 'r': set |= deviceRead case 'w': set |= deviceWrite case 'm': set |= deviceMknod } } return set } func fromSet(set uint) Permissions { var perm string if set&deviceRead == deviceRead { perm += "r" } if set&deviceWrite == deviceWrite { perm += "w" } if set&deviceMknod == deviceMknod { perm += "m" } return Permissions(perm) } // Union returns the union of the two sets of Permissions. func (p Permissions) Union(o Permissions) Permissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs | rhs) } // Difference returns the set difference of the two sets of Permissions. // In set notation, A.Difference(B) gives you A\B. func (p Permissions) Difference(o Permissions) Permissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs &^ rhs) } // Intersection computes the intersection of the two sets of Permissions. func (p Permissions) Intersection(o Permissions) Permissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs & rhs) } // IsEmpty returns whether the set of permissions in a Permissions is // empty. func (p Permissions) IsEmpty() bool { return p == Permissions("") } // IsValid returns whether the set of permissions is a subset of valid // permissions (namely, {r,w,m}). func (p Permissions) IsValid() bool { return p == fromSet(p.toSet()) } type Type rune const ( WildcardDevice Type = 'a' BlockDevice Type = 'b' CharDevice Type = 'c' // or 'u' FifoDevice Type = 'p' ) func (t Type) IsValid() bool { switch t { case WildcardDevice, BlockDevice, CharDevice, FifoDevice: return true default: return false } } func (t Type) CanMknod() bool { switch t { case BlockDevice, CharDevice, FifoDevice: return true default: return false } } func (t Type) CanCgroup() bool { switch t { case WildcardDevice, BlockDevice, CharDevice: return true default: return false } } type Rule struct { // Type of device ('c' for char, 'b' for block). If set to 'a', this rule // acts as a wildcard and all fields other than Allow are ignored. Type Type `json:"type"` // Major is the device's major number. Major int64 `json:"major"` // Minor is the device's minor number. Minor int64 `json:"minor"` // Permissions is the set of permissions that this rule applies to (in the // cgroupv1 format -- any combination of "rwm"). Permissions Permissions `json:"permissions"` // Allow specifies whether this rule is allowed. Allow bool `json:"allow"` } func (d *Rule) CgroupString() string { var ( major = strconv.FormatInt(d.Major, 10) minor = strconv.FormatInt(d.Minor, 10) ) if d.Major == Wildcard { major = "*" } if d.Minor == Wildcard { minor = "*" } return fmt.Sprintf("%c %s:%s %s", d.Type, major, minor, d.Permissions) } func (d *Rule) Mkdev() (uint64, error) { return mkDev(d) }