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

Code before:
	specFile, err := OpenSpec(bundleDir)
	if err != nil {
		return nil, fmt.Errorf("error opening spec file %q: %v", filepath.Join(bundleDir, "config.json"), err)
	}
	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.

Review comment (suggestion):
Please give the 2 errors distinctive names to differentiate them from each other, like `errStrictDecode` and `errLooseDecode` or something.

Code after:
	specFile, err := OpenSpec(bundleDir)
	if err != nil {
		return nil, fmt.Errorf("error opening spec file %q: %v", filepath.Join(bundleDir, "config.json"), err)
	}
	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.