package distworker import ( "encoding/json" "fmt" "os" "path/filepath" ) type workerState struct { Token string `json:"token"` WorkerID string `json:"worker_id"` VerificationKey string `json:"verification_key"` SigningKeyID string `json:"signing_key_id"` AccountID int64 `json:"account_id"` ConfigVersion int64 `json:"config_version"` ConfigPayload string `json:"config_payload"` } func loadWorkerState(path string) (workerState, error) { if path == "" { return workerState{}, nil } if !filepath.IsAbs(path) { return workerState{}, fmt.Errorf("worker state file must be absolute") } if info, err := os.Stat(filepath.Dir(path)); err != nil || !info.IsDir() || info.Mode().Perm() != 0o700 { return workerState{}, fmt.Errorf("worker state directory must have mode 0700") } info, err := os.Stat(path) if os.IsNotExist(err) { return workerState{}, nil } if err != nil { return workerState{}, err } if info.Mode().Perm() != 0o600 { return workerState{}, fmt.Errorf("worker state file must have mode 0600") } data, err := os.ReadFile(path) if err != nil { return workerState{}, err } var state workerState if err := json.Unmarshal(data, &state); err != nil { return workerState{}, err } return state, nil } func saveWorkerState(path string, state workerState) error { if path == "" { return nil } if !filepath.IsAbs(path) { return fmt.Errorf("worker state file must be absolute") } if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return err } if info, err := os.Stat(filepath.Dir(path)); err != nil || !info.IsDir() || info.Mode().Perm() != 0o700 { return fmt.Errorf("worker state directory must have mode 0700") } data, err := json.Marshal(state) if err != nil { return err } tmp, err := os.CreateTemp(filepath.Dir(path), ".state-") if err != nil { return err } tmpName := tmp.Name() defer os.Remove(tmpName) if err := tmp.Chmod(0o600); err != nil { _ = tmp.Close() return err } if _, err := tmp.Write(data); err != nil { _ = tmp.Close() return err } if err := tmp.Sync(); err != nil { _ = tmp.Close() return err } if err := tmp.Close(); err != nil { return err } if err := os.Rename(tmpName, path); err != nil { return err } dir, err := os.Open(filepath.Dir(path)) if err != nil { return err } defer dir.Close() return dir.Sync() }