1
1
use crate :: task:: Task ;
2
2
use anyhow:: { Context , Result } ;
3
3
use camino:: Utf8Path ;
4
+ use cap_std_ext:: cap_std:: fs:: Dir ;
5
+ use cap_std_ext:: dirext:: CapStdExtDirExt ;
4
6
use fn_error_context:: context;
5
7
use ostree_ext:: ostree:: Deployment ;
6
8
use ostree_ext:: sysroot:: SysrootLock ;
7
9
use regex:: Regex ;
10
+ use rustix:: fd:: BorrowedFd ;
8
11
use rustix:: fs:: { OFlags , ResolveFlags } ;
9
- use std:: fs;
10
12
use std:: fs:: File ;
11
13
use std:: io:: Read ;
12
14
use std:: os:: unix:: io:: AsFd ;
13
- use std:: path:: Path ;
14
15
15
16
const BOUND_IMAGE_DIR : & ' static str = "usr/lib/bootc-experimental/bound-images.d" ;
16
17
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
+
17
24
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) ?;
20
29
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) ?;
25
32
26
33
Ok ( ( ) )
27
34
}
28
35
29
36
#[ 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
+
32
42
let mut bound_images = Vec :: new ( ) ;
33
43
34
- for entry in entries {
44
+ for entry in bound_images_dir . entries ( ) ? {
35
45
//validate entry is a symlink with correct extension
36
46
let entry = entry?;
37
47
let file_name = entry. file_name ( ) ;
@@ -46,23 +56,19 @@ fn parse_spec_dir(spec_dir: &String, deployment_root: &String) -> Result<Vec<Bou
46
56
}
47
57
48
58
//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) ;
55
60
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 ,
59
64
rustix:: fs:: Mode :: empty ( ) ,
60
- ResolveFlags :: IN_ROOT ,
65
+ ResolveFlags :: IN_ROOT | ResolveFlags :: BENEATH ,
61
66
) ?
62
67
. into ( ) ;
63
68
64
69
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" ) ?;
66
72
67
73
let file_ini = ini:: Ini :: load_from_str ( & file_contents) . context ( "Parse to ini" ) ?;
68
74
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
103
109
}
104
110
105
111
#[ 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 < ( ) > {
107
113
//TODO: do this in parallel
108
114
for bound_image in bound_images {
109
115
let mut task = Task :: new ( "Pulling bound image" , "/usr/bin/podman" )
110
116
. arg ( "pull" )
111
117
. arg ( & bound_image. image ) ;
112
118
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) ;
114
120
}
115
121
task. run ( ) ?;
116
122
}
@@ -143,3 +149,26 @@ fn validate_spec_value(value: &String) -> Result<()> {
143
149
144
150
Ok ( ( ) )
145
151
}
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
+ }
0 commit comments