@@ -17,6 +17,7 @@ use std::path::Path;
17
17
use std:: process:: Command ;
18
18
use std:: str:: FromStr ;
19
19
use std:: sync:: Arc ;
20
+ use std:: time:: Duration ;
20
21
21
22
use anyhow:: Ok ;
22
23
use anyhow:: { anyhow, Context , Result } ;
@@ -230,6 +231,10 @@ pub(crate) struct InstallTargetFilesystemOpts {
230
231
#[ clap( long) ]
231
232
pub ( crate ) replace : Option < ReplaceMode > ,
232
233
234
+ /// If the target is the running system's root filesystem, this will skip any warnings.
235
+ #[ clap( long) ]
236
+ pub ( crate ) acknowledge_destructive : bool ,
237
+
233
238
/// The default mode is to "finalize" the target filesystem by invoking `fstrim` and similar
234
239
/// operations, and finally mounting it readonly. This option skips those operations. It
235
240
/// is then the responsibility of the invoking code to perform those operations.
@@ -269,6 +274,10 @@ pub(crate) struct InstallToExistingRootOpts {
269
274
#[ clap( flatten) ]
270
275
pub ( crate ) config_opts : InstallConfigOpts ,
271
276
277
+ /// Accept that this is a destructive action and skip a warning timer.
278
+ #[ clap( long) ]
279
+ pub ( crate ) acknowledge_destructive : bool ,
280
+
272
281
/// Path to the mounted root; it's expected to invoke podman with
273
282
/// `-v /:/target`, then supplying this argument is unnecessary.
274
283
#[ clap( default_value = "/target" ) ]
@@ -1373,6 +1382,34 @@ fn find_root_args_to_inherit(cmdline: &[&str], root_info: &Filesystem) -> Result
1373
1382
Ok ( RootMountInfo { mount_spec, kargs } )
1374
1383
}
1375
1384
1385
+ fn warn_on_host_root ( rootfs_fd : & Dir ) -> Result < ( ) > {
1386
+ // Seconds for which we wait while warning
1387
+ const DELAY_SECONDS : u64 = 20 ;
1388
+
1389
+ let host_root_dfd = & Dir :: open_ambient_dir ( "/proc/1/root" , cap_std:: ambient_authority ( ) ) ?;
1390
+ let host_root_devstat = rustix:: fs:: fstatvfs ( host_root_dfd) ?;
1391
+ let target_devstat = rustix:: fs:: fstatvfs ( rootfs_fd) ?;
1392
+ if host_root_devstat. f_fsid != target_devstat. f_fsid {
1393
+ tracing:: debug!( "Not the host root" ) ;
1394
+ return Ok ( ( ) ) ;
1395
+ }
1396
+ let dashes = "----------------------------" ;
1397
+ let timeout = Duration :: from_secs ( DELAY_SECONDS ) ;
1398
+ eprintln ! ( "{dashes}" ) ;
1399
+ crate :: utils:: medium_visibility_warning (
1400
+ "WARNING: This operation will OVERWRITE THE BOOTED HOST ROOT FILESYSTEM and is NOT REVERSIBLE." ,
1401
+ ) ;
1402
+ eprintln ! ( "Waiting {timeout:?} to continue; interrupt (Control-C) to cancel." ) ;
1403
+ eprintln ! ( "{dashes}" ) ;
1404
+
1405
+ let bar = indicatif:: ProgressBar :: new_spinner ( ) ;
1406
+ bar. enable_steady_tick ( Duration :: from_millis ( 100 ) ) ;
1407
+ std:: thread:: sleep ( timeout) ;
1408
+ bar. finish ( ) ;
1409
+
1410
+ Ok ( ( ) )
1411
+ }
1412
+
1376
1413
/// Implementation of the `bootc install to-filsystem` CLI command.
1377
1414
#[ context( "Installing to filesystem" ) ]
1378
1415
pub ( crate ) async fn install_to_filesystem (
@@ -1391,7 +1428,12 @@ pub(crate) async fn install_to_filesystem(
1391
1428
let rootfs_fd = Dir :: open_ambient_dir ( root_path, cap_std:: ambient_authority ( ) )
1392
1429
. with_context ( || format ! ( "Opening target root directory {root_path}" ) ) ?;
1393
1430
if let Some ( false ) = ostree_ext:: mountutil:: is_mountpoint ( & rootfs_fd, "." ) ? {
1394
- anyhow:: bail!( "Not a root mountpoint: {root_path}" ) ;
1431
+ anyhow:: bail!( "Not a mountpoint: {root_path}" ) ;
1432
+ }
1433
+
1434
+ // Check to see if this happens to be the real host root
1435
+ if !fsopts. acknowledge_destructive {
1436
+ warn_on_host_root ( & rootfs_fd) ?;
1395
1437
}
1396
1438
1397
1439
// Gather global state, destructuring the provided options
@@ -1554,6 +1596,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
1554
1596
boot_mount_spec : None ,
1555
1597
replace : opts. replace ,
1556
1598
skip_finalize : true ,
1599
+ acknowledge_destructive : opts. acknowledge_destructive ,
1557
1600
} ,
1558
1601
source_opts : opts. source_opts ,
1559
1602
target_opts : opts. target_opts ,
0 commit comments