Skip to content

Commit 06c7f4a

Browse files
committed
[Linux] Make krun_start_enter error when root directory does not exist
Previously krun_start_enter would succeed and the guest kernel would just panic. The root filesystem directory was opened lazily when the guest kernel used fuse init opcode. This commit changes it so the root directory is opened when creating the fs device. This only fixes it on Linux, but not in the macOS implementation. Signed-off-by: Matej Hrica <[email protected]>
1 parent f93f258 commit 06c7f4a

File tree

4 files changed

+47
-41
lines changed

4 files changed

+47
-41
lines changed

src/devices/src/virtio/fs/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl Fs {
9595
device_state: DeviceState::Inactive,
9696
config,
9797
shm_region: None,
98-
server: Server::new(PassthroughFs::new(fs_cfg).unwrap()),
98+
server: Server::new(PassthroughFs::new(fs_cfg).map_err(FsError::RootDirNotAccessible)?),
9999
intc: None,
100100
irq_line: None,
101101
})

src/devices/src/virtio/fs/linux/passthrough.rs

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,47 @@ pub struct PassthroughFs {
301301

302302
impl PassthroughFs {
303303
pub fn new(cfg: Config) -> io::Result<PassthroughFs> {
304+
// Initialze inodes with root directory already open
305+
let inodes = {
306+
let mut inodes = MultikeyBTreeMap::new();
307+
308+
let root = CString::new(cfg.root_dir.as_str()).expect("CString::new failed");
309+
310+
// Safe because this doesn't modify any memory and we check the return value.
311+
// We use `O_PATH` because we just want this for traversing the directory tree
312+
// and not for actually reading the contents.
313+
let fd = unsafe {
314+
libc::openat(
315+
libc::AT_FDCWD,
316+
root.as_ptr(),
317+
libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
318+
)
319+
};
320+
if fd < 0 {
321+
return Err(io::Error::last_os_error());
322+
}
323+
324+
// Safe because we just opened this fd above.
325+
let f = unsafe { File::from_raw_fd(fd) };
326+
327+
let st = stat(&f)?;
328+
329+
// Not sure why the root inode gets a refcount of 2 but that's what libfuse does.
330+
inodes.insert(
331+
fuse::ROOT_ID,
332+
InodeAltKey {
333+
ino: st.st_ino,
334+
dev: st.st_dev,
335+
},
336+
Arc::new(InodeData {
337+
inode: fuse::ROOT_ID,
338+
file: f,
339+
refcount: AtomicU64::new(2),
340+
}),
341+
);
342+
inodes
343+
};
344+
304345
let fd = if let Some(fd) = cfg.proc_sfd_rawfd {
305346
fd
306347
} else {
@@ -326,7 +367,7 @@ impl PassthroughFs {
326367
let proc_self_fd = unsafe { File::from_raw_fd(fd) };
327368

328369
Ok(PassthroughFs {
329-
inodes: RwLock::new(MultikeyBTreeMap::new()),
370+
inodes: RwLock::new(inodes),
330371
next_inode: AtomicU64::new(fuse::ROOT_ID + 2),
331372
init_inode: fuse::ROOT_ID + 1,
332373

@@ -683,48 +724,11 @@ impl FileSystem for PassthroughFs {
683724
type Handle = Handle;
684725

685726
fn init(&self, capable: FsOptions) -> io::Result<FsOptions> {
686-
let root = CString::new(self.cfg.root_dir.as_str()).expect("CString::new failed");
687-
688-
// Safe because this doesn't modify any memory and we check the return value.
689-
// We use `O_PATH` because we just want this for traversing the directory tree
690-
// and not for actually reading the contents.
691-
let fd = unsafe {
692-
libc::openat(
693-
libc::AT_FDCWD,
694-
root.as_ptr(),
695-
libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
696-
)
697-
};
698-
if fd < 0 {
699-
return Err(io::Error::last_os_error());
700-
}
701-
702-
// Safe because we just opened this fd above.
703-
let f = unsafe { File::from_raw_fd(fd) };
704-
705-
let st = stat(&f)?;
706-
707727
// Safe because this doesn't modify any memory and there is no need to check the return
708728
// value because this system call always succeeds. We need to clear the umask here because
709729
// we want the client to be able to set all the bits in the mode.
710730
unsafe { libc::umask(0o000) };
711731

712-
let mut inodes = self.inodes.write().unwrap();
713-
714-
// Not sure why the root inode gets a refcount of 2 but that's what libfuse does.
715-
inodes.insert(
716-
fuse::ROOT_ID,
717-
InodeAltKey {
718-
ino: st.st_ino,
719-
dev: st.st_dev,
720-
},
721-
Arc::new(InodeData {
722-
inode: fuse::ROOT_ID,
723-
file: f,
724-
refcount: AtomicU64::new(2),
725-
}),
726-
);
727-
728732
let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO;
729733
if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) {
730734
opts |= FsOptions::WRITEBACK_CACHE;

src/devices/src/virtio/fs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ pub enum FsError {
5959
InvalidXattrSize((u32, usize)),
6060
QueueReader(DescriptorError),
6161
QueueWriter(DescriptorError),
62+
/// The root filesystem directory is not accessible (it does not exist, permission error...)
63+
RootDirNotAccessible(io::Error),
6264
}
6365

6466
type Result<T> = std::result::Result<T, FsError>;

src/libkrun/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,8 +770,8 @@ pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 {
770770

771771
#[cfg(not(feature = "tee"))]
772772
if let Some(fs_cfg) = ctx_cfg.get_fs_cfg() {
773-
if ctx_cfg.vmr.set_fs_device(fs_cfg).is_err() {
774-
error!("Error configuring virtio-fs");
773+
if let Err(e) = ctx_cfg.vmr.set_fs_device(fs_cfg) {
774+
error!("Error configuring virtio-fs {e:?}");
775775
return -libc::EINVAL;
776776
}
777777
}

0 commit comments

Comments
 (0)