package configs import ( "errors" "fmt" "os" "strconv" "golang.org/x/sys/unix" ) const ( Wildcard = -1 ) // TODO Windows: This can be factored out in the future type Device struct { DeviceRule // 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"` } // DevicePermissions 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 DevicePermissions string const ( deviceRead uint = (1 << iota) deviceWrite deviceMknod ) func (p DevicePermissions) 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) DevicePermissions { var perm string if set&deviceRead == deviceRead { perm += "r" } if set&deviceWrite == deviceWrite { perm += "w" } if set&deviceMknod == deviceMknod { perm += "m" } return DevicePermissions(perm) } // Union returns the union of the two sets of DevicePermissions. func (p DevicePermissions) Union(o DevicePermissions) DevicePermissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs | rhs) } // Difference returns the set difference of the two sets of DevicePermissions. // In set notation, A.Difference(B) gives you A\B. func (p DevicePermissions) Difference(o DevicePermissions) DevicePermissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs &^ rhs) } // Intersection computes the intersection of the two sets of DevicePermissions. func (p DevicePermissions) Intersection(o DevicePermissions) DevicePermissions { lhs := p.toSet() rhs := o.toSet() return fromSet(lhs & rhs) } // IsEmpty returns whether the set of permissions in a DevicePermissions is // empty. func (p DevicePermissions) IsEmpty() bool { return p == DevicePermissions("") } // IsValid returns whether the set of permissions is a subset of valid // permissions (namely, {r,w,m}). func (p DevicePermissions) IsValid() bool { return p == fromSet(p.toSet()) } type DeviceType rune const ( WildcardDevice DeviceType = 'a' BlockDevice DeviceType = 'b' CharDevice DeviceType = 'c' // or 'u' FifoDevice DeviceType = 'p' ) func (t DeviceType) IsValid() bool { switch t { case WildcardDevice, BlockDevice, CharDevice, FifoDevice: return true default: return false } } func (t DeviceType) CanMknod() bool { switch t { case BlockDevice, CharDevice, FifoDevice: return true default: return false } } func (t DeviceType) CanCgroup() bool { switch t { case WildcardDevice, BlockDevice, CharDevice: return true default: return false } } type DeviceRule 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 DeviceType `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 DevicePermissions `json:"permissions"` // Allow specifies whether this rule is allowed. Allow bool `json:"allow"` } func (d *DeviceRule) 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 *DeviceRule) Mkdev() (uint64, error) { if d.Major == Wildcard || d.Minor == Wildcard { return 0, errors.New("cannot mkdev() device with wildcards") } return unix.Mkdev(uint32(d.Major), uint32(d.Minor)), nil }