Skip to content

Commit

Permalink
Merge branch 'macos_passthrough' into feat/merge_macos_passthrough
Browse files Browse the repository at this point in the history
  • Loading branch information
akitaSummer committed Feb 19, 2024
2 parents 3ed9721 + 39e1190 commit 1758719
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 129 deletions.
4 changes: 2 additions & 2 deletions src/passthrough/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,9 +806,9 @@ mod tests {
use caps::{CapSet, Capability};
use log;
use std::io::{Read, Seek, SeekFrom, Write};
#[cfg(target_os = "linux")]
use std::ops::Deref;
use std::os::unix::prelude::MetadataExt;
use tempfile::tempfile;

#[cfg(target_os = "macos")]
use std::fs;
Expand All @@ -819,7 +819,7 @@ mod tests {
use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile};

#[cfg(target_os = "macos")]
use tempfile::{tempdir, tempdir_in, NamedTempFile};
use tempfile::{tempdir, tempdir_in, tempfile, NamedTempFile};

#[cfg(target_os = "linux")]
fn prepare_passthroughfs() -> PassthroughFs {
Expand Down
12 changes: 12 additions & 0 deletions src/passthrough/stat.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
// Copyright (C) 2020-2022 Alibaba Cloud. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Fuse passthrough file system, mirroring an existing FS hierarchy.
//!
//! This file system mirrors the existing file system hierarchy of the system, starting at the
//! root file system. This is implemented by just "passing through" all requests to the
//! corresponding underlying file system.
//!
//! The code is derived from the
//! [CrosVM](https://chromium.googlesource.com/chromiumos/platform/crosvm/) project,
//! with heavy modification/enhancements from Alibaba Cloud OS team.
use std::{
ffi::{CStr, CString},
fs::File,
Expand Down
12 changes: 12 additions & 0 deletions src/passthrough/sync_io_linux.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
// Copyright (C) 2020-2022 Alibaba Cloud. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Fuse passthrough file system, mirroring an existing FS hierarchy.
//!
//! This file system mirrors the existing file system hierarchy of the system, starting at the
//! root file system. This is implemented by just "passing through" all requests to the
//! corresponding underlying file system.
//!
//! The code is derived from the
//! [CrosVM](https://chromium.googlesource.com/chromiumos/platform/crosvm/) project,
//! with heavy modification/enhancements from Alibaba Cloud OS team.
use std::{
io,
mem::{self, size_of},
Expand Down
168 changes: 41 additions & 127 deletions src/passthrough/sync_io_macos.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
use crate::{
api::{filesystem::DirEntry, CURRENT_DIR_CSTR, PARENT_DIR_CSTR},
bytes_to_cstr,
passthrough::util::einval,
};
use std::ffi::CStr;
use std::ptr;
// Copyright (C) 2020-2022 Alibaba Cloud. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Fuse passthrough file system, mirroring an existing FS hierarchy.
//!
//! This file system mirrors the existing file system hierarchy of the system, starting at the
//! root file system. This is implemented by just "passing through" all requests to the
//! corresponding underlying file system.
//!
//! The code is derived from the
//! [CrosVM](https://chromium.googlesource.com/chromiumos/platform/crosvm/) project,
//! with heavy modification/enhancements from Alibaba Cloud OS team.
use std::{
ffi::CStr,
io,
mem::{self, size_of},
os::fd::{AsRawFd, RawFd},
ptr,
};

use vm_memory::bitmap::BitmapSlice;
use vm_memory::ByteValued;

use super::{Handle, Inode, OffT, PassthroughFs};
use crate::api::filesystem::DirEntry;

#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct MacosDirent64 {
pub d_ino: u64,
pub d_seekoff: u64,
pub d_reclen: u16,
pub d_namlen: u16,
pub d_type: u8,
}
unsafe impl ByteValued for MacosDirent64 {}
use super::{Handle, Inode, OffT, PassthroughFs};

impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
pub fn do_readdir(
Expand All @@ -39,133 +36,50 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
return Ok(());
}

let mut buf = Vec::<u8>::with_capacity(size as usize);
let data = self.get_dirdata(handle, inode, libc::O_RDONLY)?;

{
// Since we are going to work with the kernel offset, we have to acquire the file lock
// for both the `lseek64` and `getdents64` syscalls to ensure that no other thread
// changes the kernel offset while we are using it.
let (guard, dir) = data.get_file_mut();

// Safe because this doesn't modify any memory and we check the return value.
let res = unsafe { libc::lseek(dir.as_raw_fd(), offset as OffT, libc::SEEK_SET) };
if res < 0 {
return Err(io::Error::last_os_error());
}

// Safe because the kernel guarantees that it will only write to `buf` and we check the
// return value.
let res = unsafe {
libc::read(
dir.as_raw_fd(),
buf.as_mut_ptr() as *mut libc::c_void,
size as libc::size_t,
)
};
if res < 0 {
let err = io::Error::last_os_error();
match err.raw_os_error() {
Some(libc::EISDIR) => {
let dir = unsafe { libc::fdopendir(dir.as_raw_fd()) };
loop {
let entry_ptr = unsafe { libc::readdir(dir) };

if entry_ptr.is_null() {
break;
}

let entry: libc::dirent = unsafe { ptr::read(entry_ptr) };

let cstr = unsafe { CStr::from_ptr(entry.d_name.as_ptr()) };
let name_str = cstr.to_str().expect("Failed to convert CStr to str");
let res = if name_str == "." || name_str == ".." {
Ok(1)
} else {
add_entry(
DirEntry {
ino: entry.d_ino,
offset: entry.d_seekoff,
type_: entry.d_type as u32,
name: cstr.to_bytes(),
},
data.borrow_fd().as_raw_fd(),
)
};
match res {
Ok(0) => break,
Ok(_) => continue,
Err(_) => return Ok(()),
}
}
unsafe { libc::closedir(dir) };
return Ok(());
}
_ => return Err(err),
}
}

// Safe because we trust the value returned by kernel.
unsafe { buf.set_len(res as usize) };

// Explicitly drop the lock so that it's not held while we fill in the fuse buffer.
mem::drop(guard);
let (_guard, dir) = data.get_file_mut();
if dir.metadata()?.is_dir() {
return Ok(());
}
// Safe because this doesn't modify any memory and we check the return value.
let res = unsafe { libc::lseek(dir.as_raw_fd(), offset as OffT, libc::SEEK_SET) };
if res < 0 {
return Err(io::Error::last_os_error());
}

let mut rem = &buf[..];
let orig_rem_len = rem.len();

while !rem.is_empty() {
debug_assert!(
rem.len() >= mem::size_of::<libc::dirent>(),
"fuse: not enough space left in `rem`"
);

let (front, back) = rem.split_at(mem::size_of::<libc::dirent>());
let dir = unsafe { libc::fdopendir(dir.as_raw_fd()) };
loop {
let entry_ptr = unsafe { libc::readdir(dir) };

let dirent = unsafe { *(front.as_ptr() as *const libc::dirent) };
if entry_ptr.is_null() {
break;
}

let namelen = dirent.d_namlen as usize;
debug_assert!(
namelen <= back.len(),
"fuse: back is smaller than `namelen`"
);
let entry: libc::dirent = unsafe { ptr::read(entry_ptr) };

let name = &back[..namelen];
let res = if name.starts_with(CURRENT_DIR_CSTR) || name.starts_with(PARENT_DIR_CSTR) {
let cstr = unsafe { CStr::from_ptr(entry.d_name.as_ptr()) };
let name_str = cstr.to_str().expect("Failed to convert CStr to str");
let res = if name_str == "." || name_str == ".." {
Ok(1)
} else {
let name = bytes_to_cstr(name)
.map_err(|e| {
error!("fuse: do_readdir: {:?}", e);
einval()
})?
.to_bytes();

add_entry(
DirEntry {
ino: dirent.d_ino,
offset: dirent.d_seekoff,
type_: dirent.d_type as u32,
name,
ino: entry.d_ino,
offset: entry.d_seekoff,
type_: entry.d_type as u32,
name: cstr.to_bytes(),
},
data.borrow_fd().as_raw_fd(),
)
};

debug_assert!(
rem.len() >= dirent.d_reclen as usize,
"fuse: rem is smaller than `d_reclen`"
);

match res {
Ok(0) => break,
Ok(_) => rem = &rem[dirent.d_reclen as usize..],
Err(e) if rem.len() == orig_rem_len => return Err(e),
Ok(_) => continue,
Err(_) => return Ok(()),
}
}

unsafe { libc::closedir(dir) };
Ok(())
}
}

0 comments on commit 1758719

Please sign in to comment.