github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/devices/v1.go (about)

     1  package devices
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"reflect"
     7  
     8  	"github.com/opencontainers/runc/libcontainer/cgroups"
     9  	"github.com/opencontainers/runc/libcontainer/configs"
    10  	"github.com/opencontainers/runc/libcontainer/devices"
    11  	"github.com/opencontainers/runc/libcontainer/userns"
    12  )
    13  
    14  var testingSkipFinalCheck bool
    15  
    16  func setV1(path string, r *configs.Resources) error {
    17  	if userns.RunningInUserNS() || r.SkipDevices {
    18  		return nil
    19  	}
    20  	// Generate two emulators, one for the current state of the cgroup and one
    21  	// for the requested state by the user.
    22  	current, err := loadEmulator(path)
    23  	if err != nil {
    24  		return err
    25  	}
    26  	target, err := buildEmulator(r.Devices)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	// Compute the minimal set of transition rules needed to achieve the
    32  	// requested state.
    33  	transitionRules, err := current.Transition(target)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	for _, rule := range transitionRules {
    38  		file := "devices.deny"
    39  		if rule.Allow {
    40  			file = "devices.allow"
    41  		}
    42  		if err := cgroups.WriteFile(path, file, rule.CgroupString()); err != nil {
    43  			return err
    44  		}
    45  	}
    46  
    47  	// Final safety check -- ensure that the resulting state is what was
    48  	// requested. This is only really correct for white-lists, but for
    49  	// black-lists we can at least check that the cgroup is in the right mode.
    50  	//
    51  	// This safety-check is skipped for the unit tests because we cannot
    52  	// currently mock devices.list correctly.
    53  	if !testingSkipFinalCheck {
    54  		currentAfter, err := loadEmulator(path)
    55  		if err != nil {
    56  			return err
    57  		}
    58  		if !target.IsBlacklist() && !reflect.DeepEqual(currentAfter, target) {
    59  			return errors.New("resulting devices cgroup doesn't precisely match target")
    60  		} else if target.IsBlacklist() != currentAfter.IsBlacklist() {
    61  			return errors.New("resulting devices cgroup doesn't match target mode")
    62  		}
    63  	}
    64  	return nil
    65  }
    66  
    67  func loadEmulator(path string) (*emulator, error) {
    68  	list, err := cgroups.ReadFile(path, "devices.list")
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	return emulatorFromList(bytes.NewBufferString(list))
    73  }
    74  
    75  func buildEmulator(rules []*devices.Rule) (*emulator, error) {
    76  	// This defaults to a white-list -- which is what we want!
    77  	emu := &emulator{}
    78  	for _, rule := range rules {
    79  		if err := emu.Apply(*rule); err != nil {
    80  			return nil, err
    81  		}
    82  	}
    83  	return emu, nil
    84  }