1use std::path::{Path, PathBuf};
2
3pub struct ZLayerDirs {
7 data_dir: PathBuf,
8}
9
10impl ZLayerDirs {
11 pub fn new(data_dir: impl Into<PathBuf>) -> Self {
13 Self {
14 data_dir: data_dir.into(),
15 }
16 }
17
18 pub fn system_default() -> Self {
20 Self::new(Self::default_data_dir())
21 }
22
23 pub fn default_data_dir() -> PathBuf {
32 #[cfg(target_os = "macos")]
33 {
34 home_dir_or_tmp().join(".zlayer")
35 }
36 #[cfg(target_os = "windows")]
37 {
38 if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
39 PathBuf::from(local_app_data).join("ZLayer")
40 } else {
41 PathBuf::from(r"C:\ProgramData\ZLayer")
42 }
43 }
44 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
45 {
46 if is_root() {
47 PathBuf::from("/var/lib/zlayer")
48 } else {
49 home_dir_or_tmp().join(".zlayer")
50 }
51 }
52 }
53
54 pub fn detect_data_dir() -> PathBuf {
60 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
61 {
62 if !is_root() {
63 let system_data = PathBuf::from("/var/lib/zlayer");
64 if system_data.join("daemon.json").exists() {
65 return system_data;
66 }
67 }
68 }
69 Self::default_data_dir()
70 }
71
72 pub fn default_run_dir() -> PathBuf {
78 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
79 {
80 PathBuf::from("/var/run/zlayer")
81 }
82 #[cfg(any(target_os = "macos", target_os = "windows"))]
83 {
84 Self::default_data_dir().join("run")
85 }
86 }
87
88 pub fn default_log_dir() -> PathBuf {
94 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
95 {
96 PathBuf::from("/var/log/zlayer")
97 }
98 #[cfg(any(target_os = "macos", target_os = "windows"))]
99 {
100 Self::default_data_dir().join("logs")
101 }
102 }
103
104 pub fn default_socket_path() -> String {
110 #[cfg(target_os = "windows")]
111 {
112 "tcp://127.0.0.1:3669".to_string()
113 }
114 #[cfg(not(target_os = "windows"))]
115 {
116 #[cfg(target_os = "macos")]
117 {
118 Self::default_data_dir()
119 .join("run")
120 .join("zlayer.sock")
121 .to_string_lossy()
122 .into_owned()
123 }
124 #[cfg(not(target_os = "macos"))]
125 {
126 "/var/run/zlayer.sock".to_string()
127 }
128 }
129 }
130
131 pub fn default_binary_dir() -> PathBuf {
140 #[cfg(unix)]
142 {
143 let probe = PathBuf::from("/usr/local/bin/.zlayer_write_probe");
144 if std::fs::write(&probe, b"").is_ok() {
145 let _ = std::fs::remove_file(&probe);
146 return PathBuf::from("/usr/local/bin");
147 }
148 }
149 let dirs = Self::system_default();
151 let bin_dir = dirs.bin();
152 let _ = std::fs::create_dir_all(&bin_dir);
153 bin_dir
154 }
155
156 pub fn data_dir(&self) -> &Path {
160 &self.data_dir
161 }
162
163 pub fn containers(&self) -> PathBuf {
165 self.data_dir.join("containers")
166 }
167
168 pub fn rootfs(&self) -> PathBuf {
170 self.data_dir.join("rootfs")
171 }
172
173 pub fn bundles(&self) -> PathBuf {
175 self.data_dir.join("bundles")
176 }
177
178 pub fn cache(&self) -> PathBuf {
180 self.data_dir.join("cache")
181 }
182
183 pub fn volumes(&self) -> PathBuf {
185 self.data_dir.join("volumes")
186 }
187
188 pub fn wasm(&self) -> PathBuf {
190 self.data_dir.join("wasm")
191 }
192
193 pub fn wasm_compiled(&self) -> PathBuf {
195 self.data_dir.join("wasm").join("compiled")
196 }
197
198 pub fn secrets(&self) -> PathBuf {
200 self.data_dir.join("secrets")
201 }
202
203 pub fn certs(&self) -> PathBuf {
205 self.data_dir.join("certs")
206 }
207
208 pub fn raft(&self) -> PathBuf {
210 self.data_dir.join("raft")
211 }
212
213 pub fn admin_password(&self) -> PathBuf {
215 self.data_dir.join("admin_password")
216 }
217
218 pub fn daemon_json(&self) -> PathBuf {
220 self.data_dir.join("daemon.json")
221 }
222
223 pub fn logs(&self) -> PathBuf {
226 self.data_dir.join("logs")
227 }
228
229 pub fn vms(&self) -> PathBuf {
233 self.data_dir.join("vms")
234 }
235
236 pub fn images(&self) -> PathBuf {
238 self.data_dir.join("images")
239 }
240
241 pub fn bin(&self) -> PathBuf {
243 self.data_dir.join("bin")
244 }
245
246 pub fn toolchain_cache(&self) -> PathBuf {
248 self.data_dir.join("toolchain-cache")
249 }
250
251 pub fn tmp(&self) -> PathBuf {
253 self.data_dir.join("tmp")
254 }
255}
256
257fn home_dir_or_tmp() -> PathBuf {
260 std::env::var_os("HOME")
261 .map(PathBuf::from)
262 .unwrap_or_else(|| PathBuf::from("/tmp"))
263}
264
265#[cfg(not(any(target_os = "macos", target_os = "windows")))]
266fn is_root() -> bool {
267 #[cfg(unix)]
268 {
269 nix::unistd::geteuid().is_root()
270 }
271 #[cfg(not(unix))]
272 {
273 false
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn subdirectories_are_relative_to_data_dir() {
283 let dirs = ZLayerDirs::new("/test/data");
284 assert_eq!(dirs.containers(), PathBuf::from("/test/data/containers"));
285 assert_eq!(dirs.rootfs(), PathBuf::from("/test/data/rootfs"));
286 assert_eq!(dirs.bundles(), PathBuf::from("/test/data/bundles"));
287 assert_eq!(dirs.cache(), PathBuf::from("/test/data/cache"));
288 assert_eq!(dirs.volumes(), PathBuf::from("/test/data/volumes"));
289 assert_eq!(dirs.wasm(), PathBuf::from("/test/data/wasm"));
290 assert_eq!(
291 dirs.wasm_compiled(),
292 PathBuf::from("/test/data/wasm/compiled")
293 );
294 assert_eq!(dirs.secrets(), PathBuf::from("/test/data/secrets"));
295 assert_eq!(dirs.certs(), PathBuf::from("/test/data/certs"));
296 assert_eq!(dirs.raft(), PathBuf::from("/test/data/raft"));
297 assert_eq!(
298 dirs.admin_password(),
299 PathBuf::from("/test/data/admin_password")
300 );
301 assert_eq!(dirs.daemon_json(), PathBuf::from("/test/data/daemon.json"));
302 assert_eq!(dirs.logs(), PathBuf::from("/test/data/logs"));
303 assert_eq!(dirs.vms(), PathBuf::from("/test/data/vms"));
304 assert_eq!(dirs.images(), PathBuf::from("/test/data/images"));
305 assert_eq!(dirs.bin(), PathBuf::from("/test/data/bin"));
306 assert_eq!(
307 dirs.toolchain_cache(),
308 PathBuf::from("/test/data/toolchain-cache")
309 );
310 assert_eq!(dirs.tmp(), PathBuf::from("/test/data/tmp"));
311 }
312
313 #[test]
314 fn system_default_uses_default_data_dir() {
315 let dirs = ZLayerDirs::system_default();
316 assert_eq!(dirs.data_dir(), ZLayerDirs::default_data_dir().as_path());
317 }
318}