github.com/elves/elvish@v0.15.0/pkg/shell/paths_unix.go (about) 1 // +build !windows,!plan9 2 3 package shell 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "syscall" 10 11 "github.com/elves/elvish/pkg/env" 12 ) 13 14 // Returns a "run directory" for storing ephemeral files, which is guaranteed 15 // to be only accessible to the current user. 16 // 17 // The path of the run directory is either $XDG_RUNTIME_DIR/elvish or 18 // $tmpdir/elvish-$uid (where $tmpdir is the system temporary directory). The 19 // former is used if the XDG_RUNTIME_DIR environment variable exists and the 20 // latter directory does not exist. 21 func getSecureRunDir() (string, error) { 22 runDirs := getRunDirCandidates() 23 for _, runDir := range runDirs { 24 if checkExclusiveAccess(runDir) { 25 return runDir, nil 26 } 27 } 28 runDir := runDirs[0] 29 err := os.MkdirAll(runDir, 0700) 30 if err != nil { 31 return "", fmt.Errorf("mkdir: %v", err) 32 } 33 if !checkExclusiveAccess(runDir) { 34 return "", fmt.Errorf("cannot create %v as a secure run directory", runDir) 35 } 36 return runDir, nil 37 } 38 39 // Returns one or more candidates for the run directory, in descending order of 40 // preference. 41 func getRunDirCandidates() []string { 42 tmpDirPath := filepath.Join(os.TempDir(), fmt.Sprintf("elvish-%d", os.Getuid())) 43 if os.Getenv(env.XDG_RUNTIME_DIR) != "" { 44 xdgDirPath := filepath.Join(os.Getenv(env.XDG_RUNTIME_DIR), "elvish") 45 return []string{xdgDirPath, tmpDirPath} 46 } 47 return []string{tmpDirPath} 48 } 49 50 func checkExclusiveAccess(runDir string) bool { 51 info, err := os.Stat(runDir) 52 if err != nil { 53 return false 54 } 55 stat := info.Sys().(*syscall.Stat_t) 56 return info.IsDir() && int(stat.Uid) == os.Getuid() && stat.Mode&077 == 0 57 }