@@ -10,11 +10,13 @@ import (
10
10
"fmt"
11
11
"os"
12
12
"path/filepath"
13
+ "strconv"
13
14
"strings"
14
15
"sync"
15
16
"syscall"
16
17
17
18
"github.com/containers/storage/pkg/unshare"
19
+ "github.com/sirupsen/logrus"
18
20
)
19
21
20
22
// Key returns the env var name for the user's home dir based on
@@ -42,18 +44,6 @@ func GetShortcutString() string {
42
44
return "~"
43
45
}
44
46
45
- // GetRuntimeDir returns XDG_RUNTIME_DIR.
46
- // XDG_RUNTIME_DIR is typically configured via pam_systemd.
47
- // GetRuntimeDir returns non-nil error if XDG_RUNTIME_DIR is not set.
48
- //
49
- // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
50
- func GetRuntimeDir () (string , error ) {
51
- if xdgRuntimeDir := os .Getenv ("XDG_RUNTIME_DIR" ); xdgRuntimeDir != "" {
52
- return filepath .EvalSymlinks (xdgRuntimeDir )
53
- }
54
- return "" , errors .New ("could not get XDG_RUNTIME_DIR" )
55
- }
56
-
57
47
// StickRuntimeDirContents sets the sticky bit on files that are under
58
48
// XDG_RUNTIME_DIR, so that the files won't be periodically removed by the system.
59
49
//
101
91
rootlessConfigHomeDirError error
102
92
rootlessConfigHomeDirOnce sync.Once
103
93
rootlessConfigHomeDir string
94
+ rootlessRuntimeDirOnce sync.Once
95
+ rootlessRuntimeDir string
104
96
)
105
97
106
98
// isWriteableOnlyByOwner checks that the specified permission mask allows write
@@ -138,3 +130,51 @@ func GetConfigHome() (string, error) {
138
130
139
131
return rootlessConfigHomeDir , rootlessConfigHomeDirError
140
132
}
133
+
134
+ // GetRuntimeDir returns a directory suitable to store runtime files.
135
+ // The function will try to use the XDG_RUNTIME_DIR env variable if it is set.
136
+ // XDG_RUNTIME_DIR is typically configured via pam_systemd.
137
+ // If XDG_RUNTIME_DIR is not set, GetRuntimeDir will try to find a suitable
138
+ // directory for the current user.
139
+ //
140
+ // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
141
+ func GetRuntimeDir () (string , error ) {
142
+ var rootlessRuntimeDirError error
143
+
144
+ rootlessRuntimeDirOnce .Do (func () {
145
+ runtimeDir := os .Getenv ("XDG_RUNTIME_DIR" )
146
+
147
+ if runtimeDir != "" {
148
+ rootlessRuntimeDir , rootlessRuntimeDirError = filepath .EvalSymlinks (runtimeDir )
149
+ return
150
+ }
151
+
152
+ uid := strconv .Itoa (unshare .GetRootlessUID ())
153
+ if runtimeDir == "" {
154
+ tmpDir := filepath .Join ("/run" , "user" , uid )
155
+ if err := os .MkdirAll (tmpDir , 0o700 ); err != nil {
156
+ logrus .Debug (err )
157
+ }
158
+ st , err := os .Lstat (tmpDir )
159
+ if err == nil && int (st .Sys ().(* syscall.Stat_t ).Uid ) == os .Geteuid () && isWriteableOnlyByOwner (st .Mode ().Perm ()) {
160
+ runtimeDir = tmpDir
161
+ }
162
+ }
163
+ if runtimeDir == "" {
164
+ tmpDir := filepath .Join (os .TempDir (), fmt .Sprintf ("storage-run-%s" , uid ))
165
+ if err := os .MkdirAll (tmpDir , 0o700 ); err != nil {
166
+ logrus .Debug (err )
167
+ }
168
+ st , err := os .Lstat (tmpDir )
169
+ if err == nil && int (st .Sys ().(* syscall.Stat_t ).Uid ) == os .Geteuid () && isWriteableOnlyByOwner (st .Mode ().Perm ()) {
170
+ runtimeDir = tmpDir
171
+ } else {
172
+ rootlessRuntimeDirError = fmt .Errorf ("path %q exists and it is not writeable only by the current user" , tmpDir )
173
+ return
174
+ }
175
+ }
176
+ rootlessRuntimeDir = runtimeDir
177
+ })
178
+
179
+ return rootlessRuntimeDir , rootlessRuntimeDirError
180
+ }
0 commit comments