@@ -68,6 +68,9 @@ pub struct WriteTarOptions {
68
68
pub selinux : bool ,
69
69
/// Allow content not in /usr; this should be paired with ostree rootfs.transient = true
70
70
pub allow_nonusr : bool ,
71
+ /// If true, do not move content in /var to /usr/share/factory/var. This should be used
72
+ /// with ostree v2024.3 or newer.
73
+ pub retain_var : bool ,
71
74
}
72
75
73
76
/// The result of writing a tar stream.
@@ -109,10 +112,16 @@ enum NormalizedPathResult<'a> {
109
112
Normal ( Utf8PathBuf ) ,
110
113
}
111
114
112
- fn normalize_validate_path (
113
- path : & Utf8Path ,
115
+ # [ derive ( Debug , Clone , PartialEq , Eq , Default ) ]
116
+ pub ( crate ) struct TarImportConfig {
114
117
allow_nonusr : bool ,
115
- ) -> Result < NormalizedPathResult < ' _ > > {
118
+ remap_factory_var : bool ,
119
+ }
120
+
121
+ fn normalize_validate_path < ' a > (
122
+ path : & ' a Utf8Path ,
123
+ config : & ' _ TarImportConfig ,
124
+ ) -> Result < NormalizedPathResult < ' a > > {
116
125
// This converts e.g. `foo//bar/./baz` into `foo/bar/baz`.
117
126
let mut components = path
118
127
. components ( )
@@ -145,14 +154,18 @@ fn normalize_validate_path(
145
154
"etc" => {
146
155
ret. push ( "usr/etc" ) ;
147
156
}
148
- // Content in /var will get copied by a systemd tmpfiles.d unit
149
157
"var" => {
150
- ret. push ( "usr/share/factory/var" ) ;
158
+ // Content in /var will get copied by a systemd tmpfiles.d unit
159
+ if config. remap_factory_var {
160
+ ret. push ( "usr/share/factory/var" ) ;
161
+ } else {
162
+ ret. push ( part)
163
+ }
151
164
}
152
165
o if EXCLUDED_TOPLEVEL_PATHS . contains ( & o) => {
153
166
return Ok ( NormalizedPathResult :: Filtered ( part) ) ;
154
167
}
155
- _ if allow_nonusr => ret. push ( part) ,
168
+ _ if config . allow_nonusr => ret. push ( part) ,
156
169
_ => {
157
170
return Ok ( NormalizedPathResult :: Filtered ( part) ) ;
158
171
}
@@ -180,7 +193,7 @@ fn normalize_validate_path(
180
193
pub ( crate ) fn filter_tar (
181
194
src : impl std:: io:: Read ,
182
195
dest : impl std:: io:: Write ,
183
- allow_nonusr : bool ,
196
+ config : & TarImportConfig ,
184
197
) -> Result < BTreeMap < String , u32 > > {
185
198
let src = std:: io:: BufReader :: new ( src) ;
186
199
let mut src = tar:: Archive :: new ( src) ;
@@ -190,7 +203,7 @@ pub(crate) fn filter_tar(
190
203
191
204
let ents = src. entries ( ) ?;
192
205
193
- tracing:: debug!( "Filtering tar; allow_nonusr={allow_nonusr }" ) ;
206
+ tracing:: debug!( "Filtering tar; config={config:? }" ) ;
194
207
195
208
// Lookaside data for dealing with hardlinked files into /sysroot; see below.
196
209
let mut changed_sysroot_objects = HashMap :: new ( ) ;
@@ -259,7 +272,7 @@ pub(crate) fn filter_tar(
259
272
}
260
273
}
261
274
262
- let normalized = match normalize_validate_path ( path, allow_nonusr ) ? {
275
+ let normalized = match normalize_validate_path ( path, config ) ? {
263
276
NormalizedPathResult :: Filtered ( path) => {
264
277
tracing:: trace!( "Filtered: {path}" ) ;
265
278
if let Some ( v) = filtered. get_mut ( path) {
@@ -282,15 +295,16 @@ pub(crate) fn filter_tar(
282
295
async fn filter_tar_async (
283
296
src : impl AsyncRead + Send + ' static ,
284
297
mut dest : impl AsyncWrite + Send + Unpin ,
285
- allow_nonusr : bool ,
298
+ config : & TarImportConfig ,
286
299
) -> Result < BTreeMap < String , u32 > > {
287
300
let ( tx_buf, mut rx_buf) = tokio:: io:: duplex ( 8192 ) ;
288
301
// The source must be moved to the heap so we know it is stable for passing to the worker thread
289
302
let src = Box :: pin ( src) ;
303
+ let config = config. clone ( ) ;
290
304
let tar_transformer = tokio:: task:: spawn_blocking ( move || {
291
305
let mut src = tokio_util:: io:: SyncIoBridge :: new ( src) ;
292
306
let dest = tokio_util:: io:: SyncIoBridge :: new ( tx_buf) ;
293
- let r = filter_tar ( & mut src, dest, allow_nonusr ) ;
307
+ let r = filter_tar ( & mut src, dest, & config ) ;
294
308
// Pass ownership of the input stream back to the caller - see below.
295
309
( r, src)
296
310
} ) ;
@@ -365,7 +379,10 @@ pub async fn write_tar(
365
379
let mut child_stdout = r. stdout . take ( ) . unwrap ( ) ;
366
380
let mut child_stderr = r. stderr . take ( ) . unwrap ( ) ;
367
381
// Copy the filtered tar stream to child stdin
368
- let filtered_result = filter_tar_async ( src, child_stdin, options. allow_nonusr ) ;
382
+ let mut import_config = TarImportConfig :: default ( ) ;
383
+ import_config. allow_nonusr = options. allow_nonusr ;
384
+ import_config. remap_factory_var = !options. retain_var ;
385
+ let filtered_result = filter_tar_async ( src, child_stdin, & import_config) ;
369
386
let output_copier = async move {
370
387
// Gather stdout/stderr to buffers
371
388
let mut child_stdout_buf = String :: new ( ) ;
@@ -421,6 +438,18 @@ mod tests {
421
438
422
439
#[ test]
423
440
fn test_normalize_path ( ) {
441
+ let imp_default = & TarImportConfig {
442
+ allow_nonusr : false ,
443
+ remap_factory_var : true ,
444
+ } ;
445
+ let allow_nonusr = & TarImportConfig {
446
+ allow_nonusr : true ,
447
+ remap_factory_var : true ,
448
+ } ;
449
+ let composefs_and_new_ostree = & TarImportConfig {
450
+ allow_nonusr : true ,
451
+ remap_factory_var : false ,
452
+ } ;
424
453
let valid_all = & [
425
454
( "/usr/bin/blah" , "./usr/bin/blah" ) ,
426
455
( "usr/bin/blah" , "./usr/bin/blah" ) ,
@@ -431,29 +460,29 @@ mod tests {
431
460
] ;
432
461
let valid_nonusr = & [ ( "boot" , "./boot" ) , ( "opt/puppet/blah" , "./opt/puppet/blah" ) ] ;
433
462
for & ( k, v) in valid_all {
434
- let r = normalize_validate_path ( k. into ( ) , false ) . unwrap ( ) ;
435
- let r2 = normalize_validate_path ( k. into ( ) , true ) . unwrap ( ) ;
463
+ let r = normalize_validate_path ( k. into ( ) , imp_default ) . unwrap ( ) ;
464
+ let r2 = normalize_validate_path ( k. into ( ) , allow_nonusr ) . unwrap ( ) ;
436
465
assert_eq ! ( r, r2) ;
437
466
match r {
438
467
NormalizedPathResult :: Normal ( r) => assert_eq ! ( r, v) ,
439
468
NormalizedPathResult :: Filtered ( o) => panic ! ( "Should not have filtered {o}" ) ,
440
469
}
441
470
}
442
471
for & ( k, v) in valid_nonusr {
443
- let strict = normalize_validate_path ( k. into ( ) , false ) . unwrap ( ) ;
472
+ let strict = normalize_validate_path ( k. into ( ) , imp_default ) . unwrap ( ) ;
444
473
assert ! (
445
474
matches!( strict, NormalizedPathResult :: Filtered ( _) ) ,
446
475
"Incorrect filter for {k}"
447
476
) ;
448
- let nonusr = normalize_validate_path ( k. into ( ) , true ) . unwrap ( ) ;
477
+ let nonusr = normalize_validate_path ( k. into ( ) , allow_nonusr ) . unwrap ( ) ;
449
478
match nonusr {
450
479
NormalizedPathResult :: Normal ( r) => assert_eq ! ( r, v) ,
451
480
NormalizedPathResult :: Filtered ( o) => panic ! ( "Should not have filtered {o}" ) ,
452
481
}
453
482
}
454
483
let filtered = & [ "/run/blah" , "/sys/foo" , "/dev/somedev" ] ;
455
484
for & k in filtered {
456
- match normalize_validate_path ( k. into ( ) , true ) . unwrap ( ) {
485
+ match normalize_validate_path ( k. into ( ) , imp_default ) . unwrap ( ) {
457
486
NormalizedPathResult :: Filtered ( _) => { }
458
487
NormalizedPathResult :: Normal ( _) => {
459
488
panic ! ( "{} should be filtered" , k)
@@ -462,9 +491,13 @@ mod tests {
462
491
}
463
492
let errs = & [ "usr/foo/../../bar" ] ;
464
493
for & k in errs {
465
- assert ! ( normalize_validate_path( k. into( ) , true ) . is_err( ) ) ;
466
- assert ! ( normalize_validate_path( k. into( ) , false ) . is_err( ) ) ;
494
+ assert ! ( normalize_validate_path( k. into( ) , allow_nonusr ) . is_err( ) ) ;
495
+ assert ! ( normalize_validate_path( k. into( ) , imp_default ) . is_err( ) ) ;
467
496
}
497
+ assert ! ( matches!(
498
+ normalize_validate_path( "var/lib/foo" . into( ) , composefs_and_new_ostree) . unwrap( ) ,
499
+ NormalizedPathResult :: Normal ( _)
500
+ ) ) ;
468
501
}
469
502
470
503
#[ tokio:: test]
@@ -481,7 +514,7 @@ mod tests {
481
514
let _ = rootfs_tar. into_inner ( ) ?;
482
515
let mut dest = Vec :: new ( ) ;
483
516
let src = tokio:: io:: BufReader :: new ( tokio:: fs:: File :: open ( rootfs_tar_path) . await ?) ;
484
- filter_tar_async ( src, & mut dest, false ) . await ?;
517
+ filter_tar_async ( src, & mut dest, & Default :: default ( ) ) . await ?;
485
518
let dest = dest. as_slice ( ) ;
486
519
let mut final_tar = tar:: Archive :: new ( Cursor :: new ( dest) ) ;
487
520
let destdir = & tempd. path ( ) . join ( "destdir" ) ;
0 commit comments