Skip to content

Commit 0ddaf3c

Browse files
committed
[macOS] Make krun_start_enter error when root dir or volumes do not exist
Signed-off-by: Matej Hrica <[email protected]>
1 parent 8ed3bd3 commit 0ddaf3c

File tree

1 file changed

+127
-100
lines changed

1 file changed

+127
-100
lines changed

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

+127-100
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct LinuxDirent64 {
7272
d_reclen: libc::c_ushort,
7373
d_ty: libc::c_uchar,
7474
}
75+
7576
unsafe impl ByteValued for LinuxDirent64 {}
7677

7778
fn ebadf() -> io::Error {
@@ -522,21 +523,143 @@ fn read_rosetta_data() -> io::Result<Vec<u8>> {
522523
Ok(data)
523524
}
524525

526+
fn insert_root_dir(
527+
root_dir: &str,
528+
inodes: &mut MultikeyBTreeMap<Inode, InodeAltKey, Arc<InodeData>>,
529+
path_cache: &mut BTreeMap<Inode, Vec<String>>,
530+
) -> io::Result<()> {
531+
let root_dir_cstr = CString::new(root_dir).expect("CString::new failed");
532+
533+
// Safe because this doesn't modify any memory and we check the return value.
534+
let fd = unsafe {
535+
libc::openat(
536+
libc::AT_FDCWD,
537+
root_dir_cstr.as_ptr(),
538+
libc::O_NOFOLLOW | libc::O_CLOEXEC,
539+
)
540+
};
541+
if fd < 0 {
542+
return Err(linux_error(io::Error::last_os_error()));
543+
}
544+
545+
// Safe because we just opened this fd above.
546+
let f = unsafe { File::from_raw_fd(fd) };
547+
548+
let st = fstat(&f)?;
549+
550+
// Safe because this doesn't modify any memory and there is no need to check the return
551+
// value because this system call always succeeds. We need to clear the umask here because
552+
// we want the client to be able to set all the bits in the mode.
553+
unsafe { libc::umask(0o000) };
554+
555+
// Not sure why the root inode gets a refcount of 2 but that's what libfuse does.
556+
inodes.insert(
557+
fuse::ROOT_ID,
558+
InodeAltKey {
559+
ino: st.st_ino,
560+
dev: st.st_dev,
561+
},
562+
Arc::new(InodeData {
563+
inode: fuse::ROOT_ID,
564+
linkdata: CString::new("").unwrap(),
565+
refcount: AtomicU64::new(2),
566+
}),
567+
);
568+
add_path(path_cache, fuse::ROOT_ID, root_dir.to_owned());
569+
Ok(())
570+
}
571+
572+
fn insert_mapped_volumes(
573+
mapped_volumes: &[(PathBuf, PathBuf)],
574+
inodes: &mut MultikeyBTreeMap<Inode, InodeAltKey, Arc<InodeData>>,
575+
next_inode: &mut u64,
576+
path_cache: &mut BTreeMap<Inode, Vec<String>>,
577+
host_volumes: &mut HashMap<String, u64>,
578+
) -> io::Result<()> {
579+
for (host_vol, guest_vol) in mapped_volumes {
580+
assert!(host_vol.is_absolute());
581+
assert!(guest_vol.is_absolute());
582+
assert_eq!(guest_vol.components().count(), 2);
583+
584+
let guest_vol_str = guest_vol
585+
.file_name()
586+
.unwrap()
587+
.to_str()
588+
.expect("Couldn't parse guest volume as String");
589+
let host_vol_str = host_vol
590+
.to_str()
591+
.expect("Couldn't parse host volume as String");
592+
let path = CString::new(host_vol_str).expect("Couldn't parse volume as CString");
593+
// Safe because this doesn't modify any memory and we check the return value.
594+
let fd = unsafe { libc::open(path.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
595+
if fd < 0 {
596+
error!(
597+
"Error setting up mapped volume: {:?}:{:?}: {:?}",
598+
host_vol,
599+
guest_vol,
600+
io::Error::last_os_error(),
601+
);
602+
continue;
603+
}
604+
605+
// Safe because we just opened this fd above.
606+
let f = unsafe { File::from_raw_fd(fd) };
607+
608+
let st = fstat(&f)?;
609+
let inode = *next_inode;
610+
*next_inode += 1;
611+
612+
inodes.insert(
613+
inode,
614+
InodeAltKey {
615+
ino: st.st_ino,
616+
dev: st.st_dev,
617+
},
618+
Arc::new(InodeData {
619+
inode,
620+
linkdata: CString::new("").unwrap(),
621+
refcount: AtomicU64::new(1),
622+
}),
623+
);
624+
add_path(path_cache, inode, host_vol_str.to_string());
625+
host_volumes.insert(guest_vol_str.to_string(), inode);
626+
}
627+
628+
Ok(())
629+
}
630+
525631
impl PassthroughFs {
526632
pub fn new(cfg: Config) -> io::Result<PassthroughFs> {
633+
let mut inodes = MultikeyBTreeMap::new();
634+
let mut next_inode = fuse::ROOT_ID + 2;
635+
let mut path_cache = BTreeMap::new();
636+
let mut host_volumes = HashMap::new();
637+
638+
insert_root_dir(&cfg.root_dir, &mut inodes, &mut path_cache)?;
639+
640+
if let Some(mapped_volumes) = &cfg.mapped_volumes {
641+
insert_mapped_volumes(
642+
mapped_volumes,
643+
&mut inodes,
644+
&mut next_inode,
645+
&mut path_cache,
646+
&mut host_volumes,
647+
)?;
648+
}
649+
527650
Ok(PassthroughFs {
528-
inodes: RwLock::new(MultikeyBTreeMap::new()),
529-
next_inode: AtomicU64::new(fuse::ROOT_ID + 2),
651+
inodes: RwLock::new(inodes),
652+
next_inode: AtomicU64::new(next_inode),
530653
init_inode: fuse::ROOT_ID + 1,
531-
path_cache: Mutex::new(BTreeMap::new()),
654+
path_cache: Mutex::new(path_cache),
532655
file_cache: Mutex::new(LruCache::new(NonZeroUsize::new(256).unwrap())),
533656
pinned_files: Mutex::new(BTreeMap::new()),
534657

535658
handles: RwLock::new(BTreeMap::new()),
536659
next_handle: AtomicU64::new(1),
537660
init_handle: 0,
538661

539-
host_volumes: RwLock::new(HashMap::new()),
662+
host_volumes: RwLock::new(host_volumes),
540663
writeback: AtomicBool::new(false),
541664

542665
rosetta_data: read_rosetta_data().ok(),
@@ -1021,102 +1144,6 @@ impl FileSystem for PassthroughFs {
10211144
type Handle = Handle;
10221145

10231146
fn init(&self, capable: FsOptions) -> io::Result<FsOptions> {
1024-
let root = CString::new(self.cfg.root_dir.as_str()).expect("CString::new failed");
1025-
1026-
// Safe because this doesn't modify any memory and we check the return value.
1027-
let fd = unsafe {
1028-
libc::openat(
1029-
libc::AT_FDCWD,
1030-
root.as_ptr(),
1031-
libc::O_NOFOLLOW | libc::O_CLOEXEC,
1032-
)
1033-
};
1034-
if fd < 0 {
1035-
return Err(linux_error(io::Error::last_os_error()));
1036-
}
1037-
1038-
// Safe because we just opened this fd above.
1039-
let f = unsafe { File::from_raw_fd(fd) };
1040-
1041-
let st = fstat(&f)?;
1042-
1043-
// Safe because this doesn't modify any memory and there is no need to check the return
1044-
// value because this system call always succeeds. We need to clear the umask here because
1045-
// we want the client to be able to set all the bits in the mode.
1046-
unsafe { libc::umask(0o000) };
1047-
1048-
let mut inodes = self.inodes.write().unwrap();
1049-
1050-
// Not sure why the root inode gets a refcount of 2 but that's what libfuse does.
1051-
inodes.insert(
1052-
fuse::ROOT_ID,
1053-
InodeAltKey {
1054-
ino: st.st_ino,
1055-
dev: st.st_dev,
1056-
},
1057-
Arc::new(InodeData {
1058-
inode: fuse::ROOT_ID,
1059-
linkdata: CString::new("").unwrap(),
1060-
refcount: AtomicU64::new(2),
1061-
}),
1062-
);
1063-
1064-
let mut path_cache = self.path_cache.lock().unwrap();
1065-
add_path(&mut path_cache, fuse::ROOT_ID, self.cfg.root_dir.clone());
1066-
1067-
if let Some(mapped_volumes) = &self.cfg.mapped_volumes {
1068-
for (host_vol, guest_vol) in mapped_volumes.iter() {
1069-
assert!(host_vol.is_absolute());
1070-
assert!(guest_vol.is_absolute());
1071-
assert_eq!(guest_vol.components().count(), 2);
1072-
1073-
let guest_vol_str = guest_vol
1074-
.file_name()
1075-
.unwrap()
1076-
.to_str()
1077-
.expect("Couldn't parse guest volume as String");
1078-
let host_vol_str = host_vol
1079-
.to_str()
1080-
.expect("Couldn't parse host volume as String");
1081-
let path = CString::new(host_vol_str).expect("Couldn't parse volume as CString");
1082-
// Safe because this doesn't modify any memory and we check the return value.
1083-
let fd = unsafe { libc::open(path.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
1084-
if fd < 0 {
1085-
error!(
1086-
"Error setting up mapped volume: {:?}:{:?}: {:?}",
1087-
host_vol,
1088-
guest_vol,
1089-
io::Error::last_os_error(),
1090-
);
1091-
continue;
1092-
}
1093-
1094-
// Safe because we just opened this fd above.
1095-
let f = unsafe { File::from_raw_fd(fd) };
1096-
1097-
let st = fstat(&f)?;
1098-
let inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
1099-
1100-
inodes.insert(
1101-
inode,
1102-
InodeAltKey {
1103-
ino: st.st_ino,
1104-
dev: st.st_dev,
1105-
},
1106-
Arc::new(InodeData {
1107-
inode,
1108-
linkdata: CString::new("").unwrap(),
1109-
refcount: AtomicU64::new(1),
1110-
}),
1111-
);
1112-
add_path(&mut path_cache, inode, host_vol_str.to_string());
1113-
self.host_volumes
1114-
.write()
1115-
.unwrap()
1116-
.insert(guest_vol_str.to_string(), inode);
1117-
}
1118-
}
1119-
11201147
let mut opts = FsOptions::empty();
11211148
if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) {
11221149
opts |= FsOptions::WRITEBACK_CACHE;

0 commit comments

Comments
 (0)