Skip to content

Commit

Permalink
container mount and umount
Browse files Browse the repository at this point in the history
Signed-off-by: Navid Yaghoobi <[email protected]>
  • Loading branch information
navidys committed Nov 2, 2024
1 parent 754791d commit 0817ce1
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 17 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fs2 = "0.4.3"
home = "0.5.9"
lazy_static = "1.5.0"
log = "0.4.22"
nix = { version = "0.29.0", features = ["mount", "user"] }
oci-client = "0.13.0"
rand = "0.8.5"
regex = "1.10.6"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ ocibuilder config --port 4444/tcp $ctr1
| containers | List the working containers and their base images.
| from | Creates a new working container either from scratch or using an image.
| images | List images in local storage.
| mount | Mounts a working container's root filesystem for manipulation.
| umount | Unmounts the root file system of the specified working containers.
| pull | Pull an image from the specified registry.
| reset | Reset local storage.
1 change: 1 addition & 0 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod config;
pub mod dist_client;
pub mod from;
pub mod mount;
pub mod oci;
pub mod pull;
pub mod reset;
140 changes: 140 additions & 0 deletions src/builder/mount.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use std::{
ffi::CStr,
fs::File,
io::{BufRead, BufReader},
path::{Path, PathBuf},
};

use log::debug;

use crate::{
error::{BuilderError, BuilderResult},
utils,
};

use super::oci::OCIBuilder;

impl OCIBuilder {
pub fn mount(&self, container: &str) -> BuilderResult<PathBuf> {
if nix::unistd::geteuid().as_raw() != 0 {
return Err(BuilderError::MountRootlessError());
}

self.lock()?;

let cnt: crate::container::containers::Container =
self.container_store().container_exist(container)?;

let top_layer = cnt.top_layer();
let top_layer_digest = utils::digest::Digest::new(&format!("sha256:{}", top_layer))?;
let mount_point = self.layer_store().overlay_merged_path(&top_layer_digest);
debug!("container {:.12} mount point: {:?}", cnt.id(), mount_point);

let workdir_path = self.layer_store().overlay_work_path(&top_layer_digest);
debug!(
"container {:.12} work directory: {:?}",
cnt.id(),
workdir_path
);

let upperdir_path = self.layer_store().overlay_diff_path(&top_layer_digest);
debug!(
"container {:.12} upper directory: {:?}",
cnt.id(),
upperdir_path
);

let mut lowerdir_paths: Vec<String> = Vec::new();

for layer in cnt.rootfs_diff() {
let layer_digest = utils::digest::Digest::new(&layer)?;
let layer_diff_path = self
.layer_store()
.overlay_diff_path(&layer_digest)
.display()
.to_string();
lowerdir_paths.push(layer_diff_path);
}

if is_mounted(&mount_point)? {
self.umount(container)?;
}

let mount_options = format!(
"lowerdir={},upperdir={},workdir={}",
lowerdir_paths.join(":"),
upperdir_path.display(),
workdir_path.display(),
);

debug!(
"container {:.12} mount options: {:?}",
cnt.id(),
mount_options
);

match nix::mount::mount(
Some(CStr::from_bytes_with_nul(b"overlay\0").unwrap()),
mount_point.display().to_string().as_bytes(),
Some(CStr::from_bytes_with_nul(b"overlay\0").unwrap()),
nix::mount::MsFlags::empty(),
Some(mount_options.as_bytes()),
) {
Ok(_) => {}
Err(err) => return Err(BuilderError::MountUmountError(err.to_string())),
}

self.unlock()?;
Ok(mount_point)
}

pub fn umount(&self, container: &str) -> BuilderResult<()> {
self.lock()?;

let cnt = self.container_store().container_exist(container)?;
let top_layer = cnt.top_layer();
let top_layer_digest = utils::digest::Digest::new(&format!("sha256:{}", top_layer))?;
let mount_point = self.layer_store().overlay_merged_path(&top_layer_digest);

if is_mounted(&mount_point)? {
debug!(
"container {:.12} filesystem umount from {:?}",
cnt.id(),
mount_point
);

match nix::mount::umount(mount_point.display().to_string().as_bytes()) {
Ok(_) => {}
Err(err) => return Err(BuilderError::MountUmountError(err.to_string())),
}
}

self.unlock()?;
Ok(())
}
}

fn is_mounted(source: &Path) -> BuilderResult<bool> {
let proc_mounts_file = "/proc/mounts";
let proc_mounts = match File::open(proc_mounts_file) {
Ok(pm) => pm,
Err(err) => return Err(BuilderError::IoError(PathBuf::from(proc_mounts_file), err)),
};

for line_result in BufReader::new(proc_mounts).lines() {
match line_result {
Ok(line) => {
let mount_info: Vec<&str> = line.split_whitespace().collect();
if !mount_info.is_empty()
&& mount_info.len() == 6
&& mount_info[1] == source.display().to_string()
{
return Ok(true);
}
}
Err(err) => return Err(BuilderError::IoError(PathBuf::from(proc_mounts_file), err)),
}
}

Ok(false)
}
9 changes: 5 additions & 4 deletions src/commands/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@ use crate::{builder, error::BuilderResult, utils};

#[derive(Parser, Debug)]
pub struct From {
image_name: String,
// image name or ID
image: String,
/// container name
#[clap(short, long)]
name: Option<String>,
}

impl From {
pub fn new(image_name: String, name: Option<String>) -> Self {
Self { image_name, name }
pub fn new(image: String, name: Option<String>) -> Self {
Self { image, name }
}

pub async fn exec(&self, root_dir: Option<OsString>) -> BuilderResult<()> {
let root_dir_path = utils::get_root_dir(root_dir);
let builder = builder::oci::OCIBuilder::new(root_dir_path)?;

let cnt_name = builder.from(&self.image_name, self.name.clone()).await?;
let cnt_name = builder.from(&self.image, self.name.clone()).await?;
println!("{}", cnt_name);

Ok(())
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod images;
pub mod mount;
pub mod pull;
pub mod reset;
pub mod umount;
12 changes: 7 additions & 5 deletions src/commands/mount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ use crate::{builder, error::BuilderResult, utils};
#[derive(Parser, Debug)]
pub struct Mount {
/// container name or ID
#[clap(short, long)]
name: Option<String>,
container: String,
}

impl Mount {
pub fn new(name: Option<String>) -> Self {
Self { name }
pub fn new(container: String) -> Self {
Self { container }
}

pub fn exec(&self, root_dir: Option<OsString>) -> BuilderResult<()> {
let root_dir_path = utils::get_root_dir(root_dir);
let _builder = builder::oci::OCIBuilder::new(root_dir_path)?;
let builder = builder::oci::OCIBuilder::new(root_dir_path)?;

let mount_point = builder.mount(&self.container)?;
println!("mount point: {:?}", mount_point);

Ok(())
}
Expand Down
12 changes: 5 additions & 7 deletions src/commands/pull.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ use crate::{builder, error::BuilderResult, utils};

#[derive(Parser, Debug)]
pub struct Pull {
image_name: String,
/// image name
image: String,
/// Using http insecure connection instead of https
#[clap(short, long)]
insecure: bool,
}

impl Pull {
pub fn new(image_name: String, insecure: bool) -> Self {
Self {
image_name,
insecure,
}
pub fn new(image: String, insecure: bool) -> Self {
Self { image, insecure }
}

pub async fn exec(&self, root_dir: Option<OsString>) -> BuilderResult<()> {
Expand All @@ -27,7 +25,7 @@ impl Pull {
let root_dir_path = utils::get_root_dir(root_dir);
let builder = builder::oci::OCIBuilder::new(root_dir_path)?;

builder.pull(&self.image_name, &self.insecure).await?;
builder.pull(&self.image, &self.insecure).await?;

Ok(())
}
Expand Down
26 changes: 26 additions & 0 deletions src/commands/umount.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::ffi::OsString;

use clap::Parser;

use crate::{builder, error::BuilderResult, utils};

#[derive(Parser, Debug)]
pub struct Umount {
/// container name or ID
container: String,
}

impl Umount {
pub fn new(container: String) -> Self {
Self { container }
}

pub fn exec(&self, root_dir: Option<OsString>) -> BuilderResult<()> {
let root_dir_path = utils::get_root_dir(root_dir);
let builder = builder::oci::OCIBuilder::new(root_dir_path)?;

builder.umount(&self.container)?;

Ok(())
}
}
13 changes: 13 additions & 0 deletions src/container/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ impl ContainerStore {
Err(BuilderError::ContainerNotFound(name_or_id.to_string()))
}

pub fn container_exist(&self, name_or_id: &str) -> BuilderResult<Container> {
let cnt_id = self.container_digest(name_or_id)?;
let cnt_list = self.containers()?;

for cnt in cnt_list {
if cnt_id.encoded == cnt.id {
return Ok(cnt);
}
}

Err(BuilderError::ContainerNotFound(name_or_id.to_string()))
}

pub fn containers_path(&self) -> PathBuf {
let mut containers_file = self.cstore_path().clone();
containers_file.push(CONTAINERS_FILENAME);
Expand Down
6 changes: 6 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ pub enum BuilderError {
#[error("invalid digest: {0}")]
InvalidDigest(String),

#[error("mount/umount error: {0}")]
MountUmountError(String),

#[error("cannot mount using driver overlay in rootless mode.")]
MountRootlessError(),

// container store errors
#[error("container store error: {0}")]
ContainerStoreError(String),
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::ffi::OsString;

use clap::{Parser, Subcommand};
use ocibuilder::commands::{config, containers, from, images, mount, pull, reset};
use ocibuilder::commands::{config, containers, from, images, mount, pull, reset, umount};

#[derive(Parser, Debug)]
#[clap(version = env!("CARGO_PKG_VERSION"), about)]
Expand Down Expand Up @@ -38,6 +38,9 @@ enum SubCommand {

/// Mounts a working container's root filesystem for manipulation
Mount(mount::Mount),

/// Unmounts the root file system of the specified working containers
Umount(umount::Umount),
}

#[tokio::main]
Expand All @@ -55,6 +58,7 @@ async fn main() {
SubCommand::Pull(pull) => pull.exec(root_dir).await,
SubCommand::Reset(reset) => reset.exec(root_dir),
SubCommand::Mount(mount) => mount.exec(root_dir),
SubCommand::Umount(umount) => umount.exec(root_dir),
};

match result {
Expand Down

0 comments on commit 0817ce1

Please sign in to comment.