package profile import ( "encoding/json" "errors" "fmt" "io" "io/fs" "os" "golang.org/x/sys/unix" ) // ReadOnlyLoad reads and validates Profile configuration without creating, // chmodding, chowning, locking, or replacing any filesystem object. func (s *FileStore) ReadOnlyLoad() (State, error) { file, err := os.OpenFile(s.path, unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_NONBLOCK, 0) if errors.Is(err, fs.ErrNotExist) { return State{}, nil } if err != nil { return State{}, fmt.Errorf("open Profile configuration read-only: %w", err) } defer file.Close() info, err := file.Stat() if err != nil { return State{}, fmt.Errorf("inspect Profile configuration: %w", err) } if !info.Mode().IsRegular() { return State{}, errors.New("Profile configuration is not a regular file") } data, err := io.ReadAll(file) if err != nil { return State{}, fmt.Errorf("read Profile configuration: %w", err) } var state State if err := json.Unmarshal(data, &state); err != nil { return State{}, fmt.Errorf("parse Profile configuration: %w", err) } for _, candidate := range state.Profiles { if _, err := ValidateAndNormalize(candidate); err != nil { return State{}, fmt.Errorf("Profile %q is invalid: %w", candidate.Name, err) } } if state.ActiveProfile != "" && indexProfile(state.Profiles, state.ActiveProfile) < 0 { return State{}, fmt.Errorf("Active Profile %q does not exist", state.ActiveProfile) } return state, nil } func indexProfile(profiles []Profile, name string) int { for i := range profiles { if profiles[i].Name == name { return i } } return -1 }