Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sdks/c/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ pub unsafe fn options_add_volume(
host_path: h,
guest_path: g,
read_only: read_only != 0,
// size cap is not exposed through the C ABI yet — the
// CLI `size=N[K|M|G]` parser is the only producer today.
// Add an `options_add_volume_size` (or extend the
// existing entry) when SDK-side size enforcement lands.
size_bytes: None,
});
}
}
Expand Down
5 changes: 5 additions & 0 deletions sdks/node/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ impl From<JsVolumeSpec> for VolumeSpec {
host_path: v.host_path,
guest_path: v.guest_path,
read_only: v.read_only.unwrap_or(false),
// size cap is not exposed through the Node API yet — add a
// `sizeBytes` field on `JsVolumeSpec` when SDK-side size
// enforcement lands. Until then JS callers can't request
// one and a `None` here is the only honest mapping.
size_bytes: None,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions sdks/python/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,11 @@ impl From<PyVolumeSpec> for VolumeSpec {
host_path: v.host,
guest_path: v.guest,
read_only: v.read_only,
// size cap is not exposed through the Python API yet — add
// a `size_bytes` field on `PyVolumeSpec` (and its
// `FromPyObject` parser) when SDK-side size enforcement
// lands.
size_bytes: None,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/boxlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ tokio-stream = "0.1.17"
term_size = "0.3"
qcow2-rs = "0.1.6"
zstd = "0.13"
nix = { version = "0.30.1", features = ["mount"] }
nix = { version = "0.30.1", features = ["mount", "fs"] }
rand = "0.9.3"
hex = "0.4.3"
signal-hook = "0.3"
Expand Down
2 changes: 2 additions & 0 deletions src/boxlite/src/jailer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,13 @@ mod tests {
host_path: "/data".to_string(),
guest_path: "/mnt/data".to_string(),
read_only: true,
size_bytes: None,
})
.with_volume(VolumeSpec {
host_path: "/output".to_string(),
guest_path: "/mnt/output".to_string(),
read_only: false,
size_bytes: None,
})
.build()
.expect("Should build successfully");
Expand Down
1 change: 1 addition & 0 deletions src/boxlite/src/jailer/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ mod tests {
host_path: "/data".to_string(),
guest_path: "/mnt/data".to_string(),
read_only: true,
size_bytes: None,
})
.build_with(sandbox)
.unwrap();
Expand Down
4 changes: 4 additions & 0 deletions src/boxlite/src/jailer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,11 +670,13 @@ mod tests {
host_path: vol_ro.to_string_lossy().to_string(),
guest_path: "/mnt/input".to_string(),
read_only: true,
size_bytes: None,
},
VolumeSpec {
host_path: vol_rw.to_string_lossy().to_string(),
guest_path: "/mnt/output".to_string(),
read_only: false,
size_bytes: None,
},
];

Expand Down Expand Up @@ -702,6 +704,7 @@ mod tests {
host_path: "/does/not/exist".to_string(),
guest_path: "/mnt/data".to_string(),
read_only: true,
size_bytes: None,
}];

let paths = build_path_access(&layout, &volumes);
Expand Down Expand Up @@ -837,6 +840,7 @@ mod tests {
host_path: vol_dir.to_string_lossy().to_string(),
guest_path: "/mnt/data".to_string(),
read_only: false,
size_bytes: None,
}])
.build()
.unwrap();
Expand Down
46 changes: 43 additions & 3 deletions src/boxlite/src/litebox/init/tasks/vmm_spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ async fn build_config(
let transport = Transport::unix(layout.socket_path());
let ready_transport = Transport::unix(layout.ready_socket_path());

let user_volumes = resolve_user_volumes(&options.volumes)?;
// Boxlite-owned dir for any sized-volume backing images we materialise
// below. Lives under the per-box home so it gets cleaned up with the box.
let sized_volumes_dir = layout.root().join("volumes");
let mkfs_bin = find_binary("mke2fs")?;
let user_volumes = resolve_user_volumes(&options.volumes, &sized_volumes_dir, &mkfs_bin)?;

// Prepare container directories (image/, rw/, rootfs/)
let container_layout = layout.shared_layout().container(container_id.as_str());
Expand Down Expand Up @@ -171,9 +175,45 @@ async fn build_config(
need_resize, // Only on fresh start with custom disk size
};

// Add user volumes via ContainerVolumeManager
// Sized user volumes → attach the ext4 image as virtio-blk and have the
// guest mount it at the SAME convention path the container init expects
// for a virtiofs-shared volume (`<SHARED>/containers/<cid>/volumes/<tag>`).
// The container's own bind mount (added below) is then identical to what
// it'd be for a regular virtiofs volume — only the source-side fs type
// differs. Process these BEFORE creating ContainerVolumeManager so its
// `&mut volume_mgr` borrow doesn't conflict with our add_block_device
// calls; remember the (volume_name, container_path, read_only) tuples
// and replay them as bind mounts on the manager afterwards.
// Same root the guest uses (see `boxlite_shared::layout::GUEST_BASE`):
// `/run/boxlite/shared`. The convention path under it is what the
// container init bind-mounts as the source.
let shared_guest = boxlite_shared::layout::SharedGuestLayout::new(
std::path::PathBuf::from(boxlite_shared::layout::GUEST_BASE).join("shared"),
);
let mut sized_binds: Vec<(String, String, bool)> = Vec::new();
for vol in user_volumes.iter().filter(|v| v.size_bytes.is_some()) {
let mount_point = shared_guest
.container(container_id.as_str())
.volume_dir(&vol.tag);
let mount_point_str = mount_point.to_string_lossy().into_owned();
volume_mgr.add_block_device(
&vol.host_path,
DiskFormat::Ext4,
vol.read_only,
Some(mount_point_str.as_str()),
false, // need_format (already formatted in resolve_user_volumes)
false, // need_resize
);
sized_binds.push((vol.tag.clone(), vol.guest_path.clone(), vol.read_only));
}

// Now the ContainerVolumeManager owns volume_mgr; replay the sized
// binds + add the legacy (virtiofs-backed) ones.
let mut container_mgr = ContainerVolumeManager::new(&mut volume_mgr);
for vol in &user_volumes {
for (volume_name, dest, ro) in &sized_binds {
container_mgr.add_bind(volume_name, dest, *ro);
}
for vol in user_volumes.iter().filter(|v| v.size_bytes.is_none()) {
container_mgr.add_volume(
container_id.as_str(),
&vol.tag,
Expand Down
Loading
Loading