src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/shell/paths_unix.go (about) 1 //go:build unix 2 3 package shell 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "syscall" 10 11 "src.elv.sh/pkg/env" 12 "src.elv.sh/pkg/fsutil" 13 ) 14 15 func defaultConfigHome() (string, error) { return homePath(".config") } 16 17 func defaultDataHome() (string, error) { return homePath(".local/share") } 18 19 var defaultDataDirs = []string{ 20 "/usr/local/share/elvish/lib", 21 "/usr/share/elvish/lib", 22 } 23 24 func defaultStateHome() (string, error) { return homePath(".local/state") } 25 26 func homePath(suffix string) (string, error) { 27 home, err := fsutil.GetHome("") 28 if err != nil { 29 return "", fmt.Errorf("resolve ~/%s: %w", suffix, err) 30 } 31 return filepath.Join(home, suffix), nil 32 } 33 34 // Returns a "run directory" for storing ephemeral files, which is guaranteed 35 // to be only accessible to the current user. 36 // 37 // The path of the run directory is either $XDG_RUNTIME_DIR/elvish or 38 // $tmpdir/elvish-$uid (where $tmpdir is the system temporary directory). The 39 // former is used if the XDG_RUNTIME_DIR environment variable exists and the 40 // latter directory does not exist. 41 func secureRunDir() (string, error) { 42 runDirs := runDirCandidates() 43 for _, runDir := range runDirs { 44 if checkExclusiveAccess(runDir) { 45 return runDir, nil 46 } 47 } 48 runDir := runDirs[0] 49 err := os.MkdirAll(runDir, 0700) 50 if err != nil { 51 return "", fmt.Errorf("mkdir: %v", err) 52 } 53 if !checkExclusiveAccess(runDir) { 54 return "", fmt.Errorf("cannot create %v as a secure run directory", runDir) 55 } 56 return runDir, nil 57 } 58 59 // Returns one or more candidates for the run directory, in descending order of 60 // preference. 61 func runDirCandidates() []string { 62 tmpDirPath := filepath.Join(os.TempDir(), fmt.Sprintf("elvish-%d", os.Getuid())) 63 if os.Getenv(env.XDG_RUNTIME_DIR) != "" { 64 xdgDirPath := filepath.Join(os.Getenv(env.XDG_RUNTIME_DIR), "elvish") 65 return []string{xdgDirPath, tmpDirPath} 66 } 67 return []string{tmpDirPath} 68 } 69 70 func checkExclusiveAccess(runDir string) bool { 71 info, err := os.Stat(runDir) 72 if err != nil { 73 return false 74 } 75 stat := info.Sys().(*syscall.Stat_t) 76 return info.IsDir() && int(stat.Uid) == os.Getuid() && stat.Mode&077 == 0 77 }