mirror of
https://github.com/nestriness/nestri.git
synced 2026-11-03 22:22:39 +02:00
fix(nesinit): hold a loop device open until its mount is made, or autoclear hands it to the next attach
This commit is contained in:
@@ -498,9 +498,9 @@ fn mount_overlay(overlay: &Overlay) -> Result<(), Failure> {
|
|||||||
// Everything that can be refused without touching the filesystem is
|
// Everything that can be refused without touching the filesystem is
|
||||||
// refused first, so a descriptor this cannot act on leaves nothing behind.
|
// refused first, so a descriptor this cannot act on leaves nothing behind.
|
||||||
let plan = OverlayPlan::new(overlay)?;
|
let plan = OverlayPlan::new(overlay)?;
|
||||||
let lower = device_for(&plan.lower, true)
|
let (lower, _lower_held) = device_for(&plan.lower, true)
|
||||||
.map_err(|error| plan.failed("the build image", &plan.lower, error))?;
|
.map_err(|error| plan.failed("the build image", &plan.lower, error))?;
|
||||||
let upper = device_for(&plan.upper, false)
|
let (upper, _upper_held) = device_for(&plan.upper, false)
|
||||||
.map_err(|error| plan.failed("the writable layer", &plan.upper, error))?;
|
.map_err(|error| plan.failed("the writable layer", &plan.upper, error))?;
|
||||||
|
|
||||||
mount_one(
|
mount_one(
|
||||||
@@ -587,7 +587,7 @@ impl ImagePlan {
|
|||||||
/// Mount one read-only image where the descriptor says.
|
/// Mount one read-only image where the descriptor says.
|
||||||
fn mount_image(image: &Image) -> Result<(), Failure> {
|
fn mount_image(image: &Image) -> Result<(), Failure> {
|
||||||
let plan = ImagePlan::new(image)?;
|
let plan = ImagePlan::new(image)?;
|
||||||
let device = device_for(&plan.device, true).map_err(|error| plan.failed(error))?;
|
let (device, _held) = device_for(&plan.device, true).map_err(|error| plan.failed(error))?;
|
||||||
mount_one(&device, &plan.at, c"erofs", plan.flags, None).map_err(|error| plan.failed(error))
|
mount_one(&device, &plan.at, c"erofs", plan.flags, None).map_err(|error| plan.failed(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,12 +642,18 @@ fn unmount_one(target: &std::ffi::CStr) -> Result<(), Failure> {
|
|||||||
/// file in a share is the host's to name, and a link would let whatever wrote
|
/// file in a share is the host's to name, and a link would let whatever wrote
|
||||||
/// it name another. Anything that is neither a block device nor a regular file
|
/// it name another. Anything that is neither a block device nor a regular file
|
||||||
/// is refused too.
|
/// is refused too.
|
||||||
fn device_for(source: &std::ffi::CStr, read_only: bool) -> io::Result<CString> {
|
///
|
||||||
|
/// A loop device comes back with its descriptor, which the caller holds until
|
||||||
|
/// the mount is made: it is attached with autoclear, so the moment nothing
|
||||||
|
/// holds it open the kernel detaches it, and the next attach is handed the
|
||||||
|
/// same device. Two attaches in a row (a build image, then its writable layer)
|
||||||
|
/// then both land on one device and the first mount reads the second file.
|
||||||
|
fn device_for(source: &std::ffi::CStr, read_only: bool) -> io::Result<(CString, Option<std::fs::File>)> {
|
||||||
use std::os::unix::fs::FileTypeExt;
|
use std::os::unix::fs::FileTypeExt;
|
||||||
let probe = open_no_symlinks(source, libc::O_RDONLY)?;
|
let probe = open_no_symlinks(source, libc::O_RDONLY)?;
|
||||||
let kind = probe.metadata()?.file_type();
|
let kind = probe.metadata()?.file_type();
|
||||||
if kind.is_block_device() {
|
if kind.is_block_device() {
|
||||||
return Ok(source.to_owned());
|
return Ok((source.to_owned(), None));
|
||||||
}
|
}
|
||||||
if !kind.is_file() {
|
if !kind.is_file() {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
@@ -656,7 +662,7 @@ fn device_for(source: &std::ffi::CStr, read_only: bool) -> io::Result<CString> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
drop(probe);
|
drop(probe);
|
||||||
loop_attach(source, read_only)
|
loop_attach(source, read_only).map(|(path, device)| (path, Some(device)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `struct open_how` from `<linux/openat2.h>`.
|
/// `struct open_how` from `<linux/openat2.h>`.
|
||||||
@@ -731,7 +737,7 @@ const LO_FLAGS_AUTOCLEAR: u32 = 4;
|
|||||||
///
|
///
|
||||||
/// Retried a few times on `EBUSY`: a device handed out as free can be taken
|
/// Retried a few times on `EBUSY`: a device handed out as free can be taken
|
||||||
/// by somebody else before it is configured.
|
/// by somebody else before it is configured.
|
||||||
fn loop_attach(file: &std::ffi::CStr, read_only: bool) -> io::Result<CString> {
|
fn loop_attach(file: &std::ffi::CStr, read_only: bool) -> io::Result<(CString, std::fs::File)> {
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
|
|
||||||
// Opened again, with the same refusal, because the file may have been
|
// Opened again, with the same refusal, because the file may have been
|
||||||
@@ -775,7 +781,7 @@ fn loop_attach(file: &std::ffi::CStr, read_only: bool) -> io::Result<CString> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if done == 0 {
|
if done == 0 {
|
||||||
return CString::new(path).map_err(io::Error::other);
|
return Ok((CString::new(path).map_err(io::Error::other)?, device));
|
||||||
}
|
}
|
||||||
last = io::Error::last_os_error();
|
last = io::Error::last_os_error();
|
||||||
if last.raw_os_error() != Some(libc::EBUSY) {
|
if last.raw_os_error() != Some(libc::EBUSY) {
|
||||||
|
|||||||
Reference in New Issue
Block a user