package devices import ( "bytes" "errors" "reflect" "github.com/moby/sys/userns" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" ) var testingSkipFinalCheck bool func setV1(path string, r *configs.Resources) error { if userns.RunningInUserNS() || r.SkipDevices { return nil } // Generate two emulators, one for the current state of the cgroup and one // for the requested state by the user. current, err := loadEmulator(path) if err != nil { return err } target, err := buildEmulator(r.Devices) if err != nil { return err } // Compute the minimal set of transition rules needed to achieve the // requested state. transitionRules, err := current.Transition(target) if err != nil { return err } for _, rule := range transitionRules { file := "devices.deny" if rule.Allow { file = "devices.allow" } if err := cgroups.WriteFile(path, file, rule.CgroupString()); err != nil { return err } } // Final safety check -- ensure that the resulting state is what was // requested. This is only really correct for white-lists, but for // black-lists we can at least check that the cgroup is in the right mode. // // This safety-check is skipped for the unit tests because we cannot // currently mock devices.list correctly. if !testingSkipFinalCheck { currentAfter, err := loadEmulator(path) if err != nil { return err } if !target.IsBlacklist() && !reflect.DeepEqual(currentAfter, target) { return errors.New("resulting devices cgroup doesn't precisely match target") } else if target.IsBlacklist() != currentAfter.IsBlacklist() { return errors.New("resulting devices cgroup doesn't match target mode") } } return nil } func loadEmulator(path string) (*emulator, error) { list, err := cgroups.ReadFile(path, "devices.list") if err != nil { return nil, err } return emulatorFromList(bytes.NewBufferString(list)) } func buildEmulator(rules []*devices.Rule) (*emulator, error) { // This defaults to a white-list -- which is what we want! emu := &emulator{} for _, rule := range rules { if err := emu.Apply(*rule); err != nil { return nil, err } } return emu, nil }