/crates/chroot/src/chroot.rs

https://github.com/pop-os/distinst · Rust · 100 lines · 80 code · 10 blank · 10 comment · 5 complexity · a48a5e149cce4f11e64f17f213219a51 MD5 · raw file

  1. use std::{
  2. ffi::OsStr,
  3. io::Result,
  4. path::{Path, PathBuf},
  5. process::Stdio,
  6. };
  7. use sys_mount::*;
  8. use crate::command::Command;
  9. /// Defines the location where a `chroot` will be performed, as well as storing
  10. /// handles to all of the binding mounts that the chroot requires.
  11. pub struct Chroot<'a> {
  12. pub path: PathBuf,
  13. dev_mount: Mount,
  14. pts_mount: Mount,
  15. proc_mount: Mount,
  16. run_mount: Mount,
  17. sys_mount: Mount,
  18. clear_envs: bool,
  19. envs: Vec<(&'a str, &'a str)>,
  20. }
  21. impl<'a> Chroot<'a> {
  22. /// Performs binding mounts of all required paths to ensure that a chroot
  23. /// is successful.
  24. pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
  25. let path = path.as_ref().canonicalize()?;
  26. let dev_mount = Mount::new("/dev", &path.join("dev"), "none", MountFlags::BIND, None)?;
  27. let pts_mount =
  28. Mount::new("/dev/pts", &path.join("dev").join("pts"), "none", MountFlags::BIND, None)?;
  29. let proc_mount = Mount::new("/proc", &path.join("proc"), "none", MountFlags::BIND, None)?;
  30. let run_mount = Mount::new("/run", &path.join("run"), "none", MountFlags::BIND, None)?;
  31. let sys_mount = Mount::new("/sys", &path.join("sys"), "none", MountFlags::BIND, None)?;
  32. Ok(Chroot {
  33. path,
  34. dev_mount,
  35. pts_mount,
  36. proc_mount,
  37. run_mount,
  38. sys_mount,
  39. clear_envs: false,
  40. envs: Vec::new(),
  41. })
  42. }
  43. /// Set an environment variable to define for this chroot.
  44. pub fn env(&mut self, key: &'a str, value: &'a str) { self.envs.push((key, value)); }
  45. /// Clear all environment variables for this chroot.
  46. pub fn clear_envs(&mut self, clear: bool) { self.clear_envs = clear; }
  47. /// Executes an external command with `chroot`.
  48. pub fn command<S: AsRef<OsStr>, T: AsRef<OsStr>, I: IntoIterator<Item = T>>(
  49. &self,
  50. cmd: S,
  51. args: I,
  52. ) -> Command {
  53. let mut command = cascade! {
  54. Command::new("chroot");
  55. ..arg(&self.path);
  56. ..arg(cmd.as_ref());
  57. ..args(args);
  58. ..stderr(Stdio::piped());
  59. ..stdout(Stdio::piped());
  60. };
  61. if self.clear_envs {
  62. command.env_clear();
  63. }
  64. for &(key, value) in &self.envs {
  65. command.env(key, value);
  66. }
  67. command
  68. }
  69. /// Return true if the filesystem was unmounted, false if it was already
  70. /// unmounted
  71. pub fn unmount(&mut self, lazy: bool) -> Result<()> {
  72. let flags = if lazy { UnmountFlags::DETACH } else { UnmountFlags::empty() };
  73. self.sys_mount.unmount(flags)?;
  74. self.run_mount.unmount(flags)?;
  75. self.proc_mount.unmount(flags)?;
  76. self.pts_mount.unmount(flags)?;
  77. self.dev_mount.unmount(flags)?;
  78. Ok(())
  79. }
  80. }
  81. impl<'a> Drop for Chroot<'a> {
  82. fn drop(&mut self) {
  83. // Ensure unmounting
  84. let _ = self.sys_mount.unmount(UnmountFlags::DETACH);
  85. let _ = self.run_mount.unmount(UnmountFlags::DETACH);
  86. let _ = self.proc_mount.unmount(UnmountFlags::DETACH);
  87. let _ = self.pts_mount.unmount(UnmountFlags::DETACH);
  88. let _ = self.dev_mount.unmount(UnmountFlags::DETACH);
  89. }
  90. }