Repository: google/gvisor
File: runsc/specutils/specutils.go (Go)

Code before:
	defer specFile.Close()
	return ReadSpecFromFile(bundleDir, specFile, conf)
}

// ReadSpecFromFile reads an OCI runtime spec from the given file. It also fixes
// up the spec so that the rest of the code doesn't need to worry about it.
//  1. Normalizes all relative paths into absolute by prepending the bundle
//     dir to them.
//  2. Looks for flag overrides and applies them if any.
//  3. Removes seccomp rules if `RuntimeDefault` was used.
func ReadSpecFromFile(bundleDir string, specFile *os.File, conf *config.Config) (*specs.Spec, error) {
	if _, err := specFile.Seek(0, io.SeekStart); err != nil {
		return nil, fmt.Errorf("error seeking to beginning of file %q: %v", specFile.Name(), err)
	}
	specBytes, err := io.ReadAll(specFile)
	if err != nil {
		return nil, fmt.Errorf("error reading spec from file %q: %v", specFile.Name(), err)
	}
	var spec specs.Spec
	decoder := json.NewDecoder(bytes.NewReader(specBytes))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&spec); err != nil {
		if err2 := json.Unmarshal(specBytes, &spec); err2 != nil {
			return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile.Name(), err2, string(specBytes))
		} else {
			log.Warningf("Problem with spec file %q: %v. Consider removing unnecessary fields.", specFile.Name(), err)
		}
	}
	if err := ValidateSpec(&spec); err != nil {
		return nil, err
	}
	if err := fixSpec(&spec, bundleDir, conf); err != nil {
		return nil, err
	}
	return &spec, nil
}

func fixSpec(spec *specs.Spec, bundleDir string, conf *config.Config) error {
	// Turn any relative paths in the spec to absolute by prepending the bundleDir.
	spec.Root.Path = absPath(bundleDir, spec.Root.Path)
	for i := range spec.Mounts {
		m := &spec.Mounts[i]
		if m.Source != "" {
			m.Source = absPath(bundleDir, m.Source)
		}
	}
	// Look for config bundle annotations and verify that they exist.
	const configBundlePrefix = "dev.gvisor.bundle."
	var bundles []config.BundleName
	for annotation, val := range spec.Annotations {
		if !strings.HasPrefix(annotation, configBundlePrefix) {

Review comment (suggestion):
This can only happen in the case where there are unknown fields, and the message should also make it clear that this isn't a fatal error because the fields will be ignored.

```
log.Warningf("OCI spec file %q contains fields unknown to `runsc`: %v. Ignoring these fields and continuing anyway.")
```

Code after:
	defer specFile.Close()
	return ReadSpecFromFile(bundleDir, specFile, conf)
}

// ReadSpecFromFile reads an OCI runtime spec from the given file. It also fixes
// up the spec so that the rest of the code doesn't need to worry about it.
//  1. Normalizes all relative paths into absolute by prepending the bundle
//     dir to them.
//  2. Looks for flag overrides and applies them if any.
//  3. Removes seccomp rules if `RuntimeDefault` was used.
func ReadSpecFromFile(bundleDir string, specFile *os.File, conf *config.Config) (*specs.Spec, error) {
	if _, err := specFile.Seek(0, io.SeekStart); err != nil {
		return nil, fmt.Errorf("error seeking to beginning of file %q: %v", specFile.Name(), err)
	}
	specBytes, err := io.ReadAll(specFile)
	if err != nil {
		return nil, fmt.Errorf("error reading spec from file %q: %v", specFile.Name(), err)
	}
	var spec specs.Spec
	decoder := json.NewDecoder(bytes.NewReader(specBytes))
	decoder.DisallowUnknownFields()
	if errLooseDecode := decoder.Decode(&spec); errLooseDecode != nil {
		if errStrictDecode := json.Unmarshal(specBytes, &spec); errStrictDecode != nil {
			return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile.Name(), errStrictDecode, string(specBytes))
		} else {
			log.Warningf("OCI spec file %q contains fields unknown to `runsc`: %v. Ignoring these fields and continuing anyway.", specFile.Name(), errLooseDecode)
		}
	}
	if err := ValidateSpec(&spec); err != nil {
		return nil, err
	}
	if err := fixSpec(&spec, bundleDir, conf); err != nil {
		return nil, err
	}
	return &spec, nil
}

func fixSpec(spec *specs.Spec, bundleDir string, conf *config.Config) error {
	// Turn any relative paths in the spec to absolute by prepending the bundleDir.
	spec.Root.Path = absPath(bundleDir, spec.Root.Path)
	for i := range spec.Mounts {
		m := &spec.Mounts[i]
		if m.Source != "" {
			m.Source = absPath(bundleDir, m.Source)
		}
	}
	// Look for config bundle annotations and verify that they exist.
	const configBundlePrefix = "dev.gvisor.bundle."
	var bundles []config.BundleName
	for annotation, val := range spec.Annotations {
		if !strings.HasPrefix(annotation, configBundlePrefix) {