Skip to content

Commit 525e601

Browse files
committed
some changes
- rework to use cap-std - add unit tests
1 parent 161641b commit 525e601

File tree

3 files changed

+57
-26
lines changed

3 files changed

+57
-26
lines changed

lib/src/boundimage.rs

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,47 @@
11
use crate::task::Task;
22
use anyhow::{Context, Result};
33
use camino::Utf8Path;
4+
use cap_std_ext::cap_std::fs::Dir;
5+
use cap_std_ext::dirext::CapStdExtDirExt;
46
use fn_error_context::context;
57
use ostree_ext::ostree::Deployment;
68
use ostree_ext::sysroot::SysrootLock;
79
use regex::Regex;
10+
use rustix::fd::BorrowedFd;
811
use rustix::fs::{OFlags, ResolveFlags};
9-
use std::fs;
1012
use std::fs::File;
1113
use std::io::Read;
1214
use std::os::unix::io::AsFd;
13-
use std::path::Path;
1415

1516
const BOUND_IMAGE_DIR: &'static str = "usr/lib/bootc-experimental/bound-images.d";
1617

18+
// Access the file descriptor for a sysroot
19+
#[allow(unsafe_code)]
20+
pub(crate) fn sysroot_fd(sysroot: &ostree_ext::ostree::Sysroot) -> BorrowedFd {
21+
unsafe { BorrowedFd::borrow_raw(sysroot.fd()) }
22+
}
23+
1724
pub(crate) fn pull_bound_images(sysroot: &SysrootLock, deployment: &Deployment) -> Result<()> {
18-
let deployment_root = format!("/{}", sysroot.deployment_dirpath(&deployment).to_string());
19-
let spec_dir = format!("{}/{BOUND_IMAGE_DIR}", deployment_root);
25+
let sysroot_fd = sysroot_fd(&sysroot);
26+
let sysroot_fd = Dir::reopen_dir(&sysroot_fd)?;
27+
let deployment_root_path = sysroot.deployment_dirpath(&deployment);
28+
let deployment_root = &sysroot_fd.open_dir(&deployment_root_path)?;
2029

21-
if Path::new(&spec_dir).exists() {
22-
let bound_images = parse_spec_dir(&spec_dir, &deployment_root)?;
23-
pull_images(deployment_root, bound_images)?;
24-
}
30+
let bound_images = parse_spec_dir(&deployment_root, BOUND_IMAGE_DIR)?;
31+
pull_images(deployment_root, bound_images)?;
2532

2633
Ok(())
2734
}
2835

2936
#[context("parse bound image spec dir")]
30-
fn parse_spec_dir(spec_dir: &String, deployment_root: &String) -> Result<Vec<BoundImage>> {
31-
let entries = fs::read_dir(spec_dir)?;
37+
fn parse_spec_dir(root: &Dir, spec_dir: &str) -> Result<Vec<BoundImage>> {
38+
let Some(bound_images_dir) = root.open_dir_optional(spec_dir)? else {
39+
return Ok(Default::default());
40+
};
41+
3242
let mut bound_images = Vec::new();
3343

34-
for entry in entries {
44+
for entry in bound_images_dir.entries()? {
3545
//validate entry is a symlink with correct extension
3646
let entry = entry?;
3747
let file_name = entry.file_name();
@@ -46,23 +56,19 @@ fn parse_spec_dir(spec_dir: &String, deployment_root: &String) -> Result<Vec<Bou
4656
}
4757

4858
//parse the file contents
49-
let file_path = entry.path();
50-
let file_path = file_path.strip_prefix(format!("/{}", deployment_root)).context("Prefix not found in file path")?;
51-
52-
let root_dir = File::open(deployment_root).context("Unable to open deployment_root")?;
53-
let root_fd = root_dir.as_fd();
54-
59+
let path = Utf8Path::new(spec_dir).join(file_name);
5560
let mut file: File = rustix::fs::openat2(
56-
root_fd,
57-
file_path,
58-
OFlags::empty(),
61+
root.as_fd(),
62+
path.as_std_path(),
63+
OFlags::CLOEXEC | OFlags::RDONLY,
5964
rustix::fs::Mode::empty(),
60-
ResolveFlags::IN_ROOT,
65+
ResolveFlags::IN_ROOT | ResolveFlags::BENEATH,
6166
)?
6267
.into();
6368

6469
let mut file_contents = String::new();
65-
file.read_to_string(&mut file_contents).context("Unable to read file contents")?;
70+
file.read_to_string(&mut file_contents)
71+
.context("Unable to read file contents")?;
6672

6773
let file_ini = ini::Ini::load_from_str(&file_contents).context("Parse to ini")?;
6874
let file_extension = Utf8Path::new(file_name).extension();
@@ -103,14 +109,14 @@ fn parse_container_file(file_name: &str, file_contents: &ini::Ini) -> Result<Bou
103109
}
104110

105111
#[context("pull bound images")]
106-
fn pull_images(deployment_root: String, bound_images: Vec<BoundImage>) -> Result<()> {
112+
fn pull_images(_deployment_root: &Dir, bound_images: Vec<BoundImage>) -> Result<()> {
107113
//TODO: do this in parallel
108114
for bound_image in bound_images {
109115
let mut task = Task::new("Pulling bound image", "/usr/bin/podman")
110116
.arg("pull")
111117
.arg(&bound_image.image);
112118
if let Some(auth_file) = &bound_image.auth_file {
113-
task = task.arg("--authfile").arg(format!("/{deployment_root}/{auth_file}"));
119+
task = task.arg("--authfile").arg(auth_file);
114120
}
115121
task.run()?;
116122
}
@@ -143,3 +149,26 @@ fn validate_spec_value(value: &String) -> Result<()> {
143149

144150
Ok(())
145151
}
152+
153+
#[cfg(test)]
154+
mod tests {
155+
use super::*;
156+
use cap_std_ext::cap_std;
157+
158+
#[test]
159+
fn test_parse_bound_images() -> Result<()> {
160+
let td = &cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
161+
let images = parse_spec_dir(td, &BOUND_IMAGE_DIR).unwrap();
162+
assert_eq!(images.len(), 0);
163+
164+
td.create_dir_all(BOUND_IMAGE_DIR).unwrap();
165+
let images = parse_spec_dir(td, &BOUND_IMAGE_DIR).unwrap();
166+
assert_eq!(images.len(), 0);
167+
168+
td.symlink("../blah", format!("{BOUND_IMAGE_DIR}/foo.image"))
169+
.unwrap();
170+
assert!(parse_spec_dir(td, &BOUND_IMAGE_DIR).is_err());
171+
172+
Ok(())
173+
}
174+
}

lib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#![allow(clippy::needless_borrow)]
1818
#![allow(clippy::needless_borrows_for_generic_args)]
1919

20-
pub mod cli;
2120
mod boundimage;
21+
pub mod cli;
2222
pub(crate) mod deploy;
2323
pub(crate) mod generator;
2424
mod image;

lib/src/task.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::{
2-
ffi::OsStr, io::{Seek, Write}, process::{Command, Stdio}
2+
ffi::OsStr,
3+
io::{Seek, Write},
4+
process::{Command, Stdio},
35
};
46

57
use anyhow::{Context, Result};

0 commit comments

Comments
 (0)