1use super::Result;
4use anyhow::Context;
5use camino::{Utf8Path, Utf8PathBuf};
6use cap_std_ext::RootDir;
7use cap_std_ext::cap_std::fs::Dir;
8use fn_error_context::context;
9use ostree::glib;
10use std::fs::File;
11use std::sync::OnceLock;
12
13struct ConfigPaths {
14 persistent: Utf8PathBuf,
15 runtime: Utf8PathBuf,
16 system: Option<Utf8PathBuf>,
17}
18
19fn get_config_paths(root: bool) -> &'static ConfigPaths {
23 if root {
24 static PATHS_ROOT: OnceLock<ConfigPaths> = OnceLock::new();
25 PATHS_ROOT.get_or_init(|| ConfigPaths::new("etc", "run", Some("usr/lib")))
26 } else {
27 static PATHS_USER: OnceLock<ConfigPaths> = OnceLock::new();
28 PATHS_USER.get_or_init(|| {
29 ConfigPaths::new(
30 Utf8PathBuf::try_from(glib::user_config_dir()).unwrap(),
31 Utf8PathBuf::try_from(glib::user_runtime_dir()).unwrap(),
32 None,
33 )
34 })
35 }
36}
37
38impl ConfigPaths {
39 fn new<P: AsRef<Utf8Path>>(persistent: P, runtime: P, system: Option<P>) -> Self {
40 fn relative_owned(p: &Utf8Path) -> Utf8PathBuf {
41 p.as_str().trim_start_matches('/').into()
42 }
43 let mut r = ConfigPaths {
44 persistent: relative_owned(persistent.as_ref()),
45 runtime: relative_owned(runtime.as_ref()),
46 system: system.as_ref().map(|s| relative_owned(s.as_ref())),
47 };
48 let path = "ostree";
49 r.persistent.push(path);
50 r.runtime.push(path);
51 if let Some(system) = r.system.as_mut() {
52 system.push(path);
53 }
54 r
55 }
56
57 pub(crate) fn open_file(
59 &self,
60 root: &RootDir,
61 p: impl AsRef<Utf8Path>,
62 ) -> Result<Option<(Utf8PathBuf, File)>> {
63 let p = p.as_ref();
64 let mut runtime = self.runtime.clone();
65 runtime.push(p);
66 if let Some(f) = root
67 .open_optional(&runtime)
68 .context("Opening runtime auth file")?
69 {
70 return Ok(Some((runtime, f)));
71 }
72 let mut persistent = self.persistent.clone();
73 persistent.push(p);
74 if let Some(f) = root
75 .open_optional(&persistent)
76 .context("Opening persistent auth file")?
77 {
78 return Ok(Some((persistent, f)));
79 }
80 if let Some(mut system) = self.system.clone() {
81 system.push(p);
82 if let Some(f) = root
83 .open_optional(&system)
84 .context("Opening system auth file")?
85 {
86 return Ok(Some((system, f)));
87 }
88 }
89 Ok(None)
90 }
91}
92
93#[context("Loading global authfile")]
95pub fn get_global_authfile(root: &Dir) -> Result<Option<(Utf8PathBuf, File)>> {
96 let root = &RootDir::new(root, ".").context("Opening RootDir")?;
97 let am_uid0 = rustix::process::getuid() == rustix::process::Uid::ROOT;
98 get_global_authfile_impl(root, am_uid0)
99}
100
101fn get_global_authfile_impl(root: &RootDir, am_uid0: bool) -> Result<Option<(Utf8PathBuf, File)>> {
103 let paths = get_config_paths(am_uid0);
104 paths.open_file(root, "auth.json")
105}
106
107#[cfg(test)]
108mod tests {
109 use std::io::Read;
110
111 use super::*;
112 use camino::Utf8PathBuf;
113 use cap_std_ext::{cap_std, cap_tempfile};
114
115 fn read_authfile(
116 root: &cap_std_ext::RootDir,
117 am_uid0: bool,
118 ) -> Result<Option<(Utf8PathBuf, String)>> {
119 let r = get_global_authfile_impl(root, am_uid0)?;
120 if let Some((path, mut f)) = r {
121 let mut s = String::new();
122 f.read_to_string(&mut s)?;
123 Ok(Some((path.try_into()?, s)))
124 } else {
125 Ok(None)
126 }
127 }
128
129 #[test]
130 fn test_config_paths() -> Result<()> {
131 let root = &cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
132 let rootdir = &RootDir::new(root, ".")?;
133 assert!(read_authfile(rootdir, true).unwrap().is_none());
134 root.create_dir_all("etc/ostree")?;
135 root.write("etc/ostree/auth.json", "etc ostree auth")?;
136 let (p, authdata) = read_authfile(rootdir, true).unwrap().unwrap();
137 assert_eq!(p, "etc/ostree/auth.json");
138 assert_eq!(authdata, "etc ostree auth");
139 root.create_dir_all("usr/lib/ostree")?;
140 root.write("usr/lib/ostree/auth.json", "usrlib ostree auth")?;
141 let (p, authdata) = read_authfile(rootdir, true).unwrap().unwrap();
143 assert_eq!(p, "etc/ostree/auth.json");
144 assert_eq!(authdata, "etc ostree auth");
145 root.remove_file("etc/ostree/auth.json")?;
147 let (p, authdata) = read_authfile(rootdir, true).unwrap().unwrap();
148 assert_eq!(p, "usr/lib/ostree/auth.json");
149 assert_eq!(authdata, "usrlib ostree auth");
150
151 root.create_dir_all("etc/containers")?;
153 root.write("etc/containers/auth.json", "etc containers ostree auth")?;
154 root.symlink_contents("../containers/auth.json", "etc/ostree/auth.json")?;
155 let (p, authdata) = read_authfile(rootdir, true).unwrap().unwrap();
156 assert_eq!(p, "etc/ostree/auth.json");
157 assert_eq!(authdata, "etc containers ostree auth");
158 root.remove_file("etc/ostree/auth.json")?;
160 root.symlink_contents("/etc/containers/auth.json", "etc/ostree/auth.json")?;
161 assert_eq!(p, "etc/ostree/auth.json");
162 assert_eq!(authdata, "etc containers ostree auth");
163
164 let mut user_runtime_dir =
166 Utf8Path::from_path(glib::user_runtime_dir().strip_prefix("/").unwrap())
167 .unwrap()
168 .to_path_buf();
169 user_runtime_dir.push("ostree");
170 root.create_dir_all(&user_runtime_dir)?;
171 user_runtime_dir.push("auth.json");
172 root.write(&user_runtime_dir, "usr_runtime_dir ostree auth")?;
173
174 let mut user_config_dir =
175 Utf8Path::from_path(glib::user_config_dir().strip_prefix("/").unwrap())
176 .unwrap()
177 .to_path_buf();
178 user_config_dir.push("ostree");
179 root.create_dir_all(&user_config_dir)?;
180 user_config_dir.push("auth.json");
181 root.write(&user_config_dir, "usr_config_dir ostree auth")?;
182
183 let (p, authdata) = read_authfile(rootdir, false).unwrap().unwrap();
185 assert_eq!(p, user_runtime_dir);
186 assert_eq!(authdata, "usr_runtime_dir ostree auth");
187
188 root.remove_file(&user_runtime_dir)?;
190 let (p, authdata) = read_authfile(rootdir, false).unwrap().unwrap();
191 assert_eq!(p, user_config_dir);
192 assert_eq!(authdata, "usr_config_dir ostree auth");
193
194 Ok(())
195 }
196}