File: paths_unix.go

package info (click to toggle)
elvish 0.21.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,372 kB
  • sloc: javascript: 236; sh: 130; python: 104; makefile: 88; xml: 9
file content (77 lines) | stat: -rw-r--r-- 2,099 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//go:build unix

package shell

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"src.elv.sh/pkg/env"
	"src.elv.sh/pkg/fsutil"
)

func defaultConfigHome() (string, error) { return homePath(".config") }

func defaultDataHome() (string, error) { return homePath(".local/share") }

var defaultDataDirs = []string{
	"/usr/local/share/elvish/lib",
	"/usr/share/elvish/lib",
}

func defaultStateHome() (string, error) { return homePath(".local/state") }

func homePath(suffix string) (string, error) {
	home, err := fsutil.GetHome("")
	if err != nil {
		return "", fmt.Errorf("resolve ~/%s: %w", suffix, err)
	}
	return filepath.Join(home, suffix), nil
}

// Returns a "run directory" for storing ephemeral files, which is guaranteed
// to be only accessible to the current user.
//
// The path of the run directory is either $XDG_RUNTIME_DIR/elvish or
// $tmpdir/elvish-$uid (where $tmpdir is the system temporary directory). The
// former is used if the XDG_RUNTIME_DIR environment variable exists and the
// latter directory does not exist.
func secureRunDir() (string, error) {
	runDirs := runDirCandidates()
	for _, runDir := range runDirs {
		if checkExclusiveAccess(runDir) {
			return runDir, nil
		}
	}
	runDir := runDirs[0]
	err := os.MkdirAll(runDir, 0700)
	if err != nil {
		return "", fmt.Errorf("mkdir: %v", err)
	}
	if !checkExclusiveAccess(runDir) {
		return "", fmt.Errorf("cannot create %v as a secure run directory", runDir)
	}
	return runDir, nil
}

// Returns one or more candidates for the run directory, in descending order of
// preference.
func runDirCandidates() []string {
	tmpDirPath := filepath.Join(os.TempDir(), fmt.Sprintf("elvish-%d", os.Getuid()))
	if os.Getenv(env.XDG_RUNTIME_DIR) != "" {
		xdgDirPath := filepath.Join(os.Getenv(env.XDG_RUNTIME_DIR), "elvish")
		return []string{xdgDirPath, tmpDirPath}
	}
	return []string{tmpDirPath}
}

func checkExclusiveAccess(runDir string) bool {
	info, err := os.Stat(runDir)
	if err != nil {
		return false
	}
	stat := info.Sys().(*syscall.Stat_t)
	return info.IsDir() && int(stat.Uid) == os.Getuid() && stat.Mode&077 == 0
}