-
Notifications
You must be signed in to change notification settings - Fork 397
/
os.go
132 lines (115 loc) · 3.16 KB
/
os.go
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package fpath
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/zeebo/errs"
)
// IsRoot returns whether path is the root directory
func IsRoot(path string) bool {
abs, err := filepath.Abs(path)
if err == nil {
path = abs
}
return filepath.Dir(path) == path
}
// ApplicationDir returns best base directory for specific OS
func ApplicationDir(subdir ...string) string {
for i := range subdir {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
subdir[i] = strings.Title(subdir[i])
} else {
subdir[i] = strings.ToLower(subdir[i])
}
}
var appdir string
home := os.Getenv("HOME")
switch runtime.GOOS {
case "windows":
// Windows standards: https://msdn.microsoft.com/en-us/library/windows/apps/hh465094.aspx?f=255&MSPPError=-2147217396
for _, env := range []string{"AppData", "AppDataLocal", "UserProfile", "Home"} {
val := os.Getenv(env)
if val != "" {
appdir = val
break
}
}
case "darwin":
// Mac standards: https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html
appdir = filepath.Join(home, "Library", "Application Support")
case "linux":
fallthrough
default:
// Linux standards: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
appdir = os.Getenv("XDG_DATA_HOME")
if appdir == "" && home != "" {
appdir = filepath.Join(home, ".local", "share")
}
}
return filepath.Join(append([]string{appdir}, subdir...)...)
}
// IsValidSetupDir checks if directory is valid for setup configuration
func IsValidSetupDir(name string) (ok bool, err error) {
_, err = os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return true, err
}
return false, err
}
f, err := os.Open(name)
if err != nil {
return false, err
}
defer func() {
err = errs.Combine(err, f.Close())
}()
for {
var filenames []string
filenames, err = f.Readdirnames(100)
if err == io.EOF {
// nothing more
return true, nil
} else if err != nil {
// something went wrong
return false, err
}
for _, filename := range filenames {
if filename == "config.yaml" {
return false, nil
}
}
}
}
// IsWritable determines if a directory is writeable
func IsWritable(filepath string) (bool, error) {
info, err := os.Stat(filepath)
if err != nil {
return false, err
}
if !info.IsDir() {
return false, fmt.Errorf("Path %s is not a directory", filepath)
}
// Check if the user bit is enabled in file permission
if info.Mode().Perm()&0200 == 0 {
return false, fmt.Errorf("Write permission bit is not set on this file for user")
}
// Test if user can create file
// There is no OS cross-compatible method for
// determining if a user has write permissions on a folder.
// We can test by attempting to create a file in the folder.
testFile := path.Join(filepath, ".perm")
file, err := os.Create(testFile) // For read access.
if err != nil {
return false, fmt.Errorf("Write permission bit is not set on this file for user")
}
_ = file.Close()
_ = os.Remove(testFile)
return true, nil
}