package windsurf import ( "os" "path/filepath" "runtime" ) // dataDirStat reports whether a data directory path exists and is writable. type dataDirStat struct { Exists bool Writable bool } // dataDirStatFn is replaced in tests to avoid touching the filesystem. var dataDirStatFn = defaultDataDirStat // userConfigDirFn is replaced in tests. var userConfigDirFn = defaultUserConfigDir func defaultDataDirStat(path string) dataDirStat { info, err := os.Stat(path) if err != nil || !info.IsDir() { return dataDirStat{} } // Probe writability by creating a sentinel file. This catches root-owned // directories on Linux that are readable but not user-writable. tmp := filepath.Join(path, ".sub2api-writable-check") f, err := os.Create(tmp) if err != nil { return dataDirStat{Exists: true} } _ = f.Close() _ = os.Remove(tmp) return dataDirStat{Exists: true, Writable: true} } func defaultUserConfigDir() string { if dir, err := os.UserConfigDir(); err == nil { return dir } return "" } // LegacyLinuxDataDir is the original pre-cross-platform data directory. // Preserved to keep existing Linux deployments functional after upgrade. const LegacyLinuxDataDir = "/opt/windsurf/data" // resolveDataDir picks the base directory under which each LS instance's // per-key subdirectory will live. // // Priority: // 1. cfg.DataDir — explicit caller wins // 2. WINDSURF_DATA_DIR env var // 3. /opt/windsurf/data if it exists AND is writable (Linux backward compat) // 4. os.UserConfigDir()/windsurf/data (cross-platform default) // 5. os.TempDir()/windsurf-data (last-resort fallback) func resolveDataDir(cfg LSPoolConfig) string { return resolveDataDirFor(cfg.DataDir, os.Getenv("WINDSURF_DATA_DIR"), runtime.GOOS) } func resolveDataDirFor(cfgDir, envDir, goos string) string { if cfgDir != "" { return cfgDir } if envDir != "" { return envDir } if goos == "linux" { if stat := dataDirStatFn(LegacyLinuxDataDir); stat.Exists && stat.Writable { return LegacyLinuxDataDir } } if cfgDir := userConfigDirFn(); cfgDir != "" { return filepath.Join(cfgDir, "windsurf", "data") } return filepath.Join(os.TempDir(), "windsurf-data") } // instanceHomeDir returns the per-instance HOME sandbox path. The LS binary // writes telemetry and caches under $HOME (Linux/macOS) or %USERPROFILE% // (Windows), so isolating it per instance prevents one instance from leaking // state into another or into the invoking user's real home directory. func instanceHomeDir(instanceDataDir string) string { return filepath.Join(instanceDataDir, "home") } // homeEnvForPlatform returns the set of environment assignments needed to // direct the LS process at the given home directory. On Windows we also set // USERPROFILE because most Windows programs check that first. func homeEnvForPlatform(homeDir, goos string) []string { envs := []string{"HOME=" + homeDir} if goos == "windows" { envs = append(envs, "USERPROFILE="+homeDir) } return envs }