use crate::vfs::{ VfsError, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat, VirtualUtimeSpec,};use getrandom::getrandom;use web_time::{SystemTime, UNIX_EPOCH}; const DEVICE_PATHS: &[&str] = &[ "/dev/null", "/dev/zero", "/dev/stdin", "/dev/stdout", "/dev/stderr", "/dev/urandom",]; const DEVICE_DIRS: &[&str] = &["/dev/fd", "/dev/pts"];const DEFAULT_STREAM_DEVICE_READ_BYTES: usize = 4096;const DEV_DIR_ENTRIES: &[(&str, bool)] = &[ ("null", false), ("zero", false), ("stdin", false), ("stdout", false), ("stderr", false), ("urandom", false), ("fd", true),]; #[derive(Debug, Clone)]pub struct DeviceLayer<V> { inner: V,} pub fn create_device_layer<V>(vfs: V) -> DeviceLayer<V> { DeviceLayer { inner: vfs }} impl<V> DeviceLayer<V> { pub fn into_inner(self) -> V { self.inner } pub fn inner(&self) -> &V { &self.inner } pub fn inner_mut(&mut self) -> &mut V { &mut self.inner }} impl<V: VirtualFileSystem> VirtualFileSystem for DeviceLayer<V> { fn read_file(&mut self, path: &str) -> VfsResult<Vec<u8>> { if let Some(bytes) = read_stream_device(path, DEFAULT_STREAM_DEVICE_READ_BYTES) { return bytes; } if self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(Vec::new()); } self.inner.read_file(path) } fn read_dir(&mut self, path: &str) -> VfsResult<Vec<String>> { if path == "/dev" { return Ok(DEV_DIR_ENTRIES .iter() .map(|(name, _)| String::from(*name)) .collect()); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir(path) } fn read_dir_limited(&mut self, path: &str, max_entries: usize) -> VfsResult<Vec<String>> { if path == "/dev" { let entries = DEV_DIR_ENTRIES .iter() .map(|(name, _)| String::from(*name)) .collect::<Vec<_>>(); if entries.len() > max_entries { return Err(VfsError::new( "ENOMEM", format!( "directory listing for '{path}' exceeds configured limit of {max_entries} entries" ), )); } return Ok(entries); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir_limited(path, max_entries) } fn read_dir_with_types(&mut self, path: &str) -> VfsResult<Vec<VirtualDirEntry>> { if path == "/dev" { return Ok(DEV_DIR_ENTRIES .iter() .map(|(name, is_directory)| VirtualDirEntry { name: String::from(*name), is_directory: *is_directory, is_symbolic_link: false, }) .collect()); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir_with_types(path) } fn write_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = content.into(); return Ok(()); } self.inner.write_file(path, content) } fn create_file_exclusive(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { let _ = content.into(); return Err(VfsError::new( "EEXIST", format!("file already exists, open '{path}'"), )); } self.inner.create_file_exclusive(path, content) } fn append_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<u64> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(content.into().len() as u64); } self.inner.append_file(path, content) } fn create_dir(&mut self, path: &str) -> VfsResult<()> { if is_device_dir(path) { return Ok(()); } self.inner.create_dir(path) } fn mkdir(&mut self, path: &str, recursive: bool) -> VfsResult<()> { if is_device_dir(path) { return Ok(()); } self.inner.mkdir(path, recursive) } fn mknod(&mut self, path: &str, mode: u32, rdev: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EEXIST", format!("device already exists: {path}"), )); } self.inner.mknod(path, mode, rdev) } fn exists(&self, path: &str) -> bool { if is_device_path(path) || is_device_dir(path) { return true; } self.inner.exists(path) } fn stat(&mut self, path: &str) -> VfsResult<VirtualStat> { if is_device_path(path) { return Ok(device_stat(path)); } if is_device_dir(path) { return Ok(device_dir_stat(path)); } self.inner.stat(path) } fn remove_file(&mut self, path: &str) -> VfsResult<()> { if is_device_path(path) { return Err(VfsError::permission_denied("unlink", path)); } self.inner.remove_file(path) } fn remove_dir(&mut self, path: &str) -> VfsResult<()> { if is_device_dir(path) { return Err(VfsError::permission_denied("rmdir", path)); } self.inner.remove_dir(path) } fn rename(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> { if is_device_path(old_path) || is_device_path(new_path) { return Err(VfsError::permission_denied("rename", old_path)); } self.inner.rename(old_path, new_path) } fn realpath(&self, path: &str) -> VfsResult<String> { if is_device_path(path) || is_device_dir(path) { return Ok(String::from(path)); } self.inner.realpath(path) } fn symlink(&mut self, target: &str, link_path: &str) -> VfsResult<()> { self.inner.symlink(target, link_path) } fn read_link(&self, path: &str) -> VfsResult<String> { self.inner.read_link(path) } fn lstat(&self, path: &str) -> VfsResult<VirtualStat> { if is_device_path(path) { return Ok(device_stat(path)); } if is_device_dir(path) { return Ok(device_dir_stat(path)); } self.inner.lstat(path) } fn link(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> { if is_device_path(old_path) { return Err(VfsError::permission_denied("link", old_path)); } self.inner.link(old_path, new_path) } fn chmod(&mut self, path: &str, mode: u32) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chmod(path, mode) } fn chown(&mut self, path: &str, uid: u32, gid: u32) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chown(path, uid, gid) } fn chown_spec( &mut self, path: &str, uid: u32, gid: u32, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chown_spec(path, uid, gid, follow_symlinks) } fn get_xattr(&mut self, path: &str, name: &str, follow_symlinks: bool) -> VfsResult<Vec<u8>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.get_xattr(path, name, follow_symlinks) } fn list_xattrs(&mut self, path: &str, follow_symlinks: bool) -> VfsResult<Vec<String>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.list_xattrs(path, follow_symlinks) } fn set_xattr( &mut self, path: &str, name: &str, value: Vec<u8>, flags: u32, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner .set_xattr(path, name, value, flags, follow_symlinks) } fn remove_xattr(&mut self, path: &str, name: &str, follow_symlinks: bool) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.remove_xattr(path, name, follow_symlinks) } fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.utimes(path, atime_ms, mtime_ms) } fn utimes_spec( &mut self, path: &str, atime: VirtualUtimeSpec, mtime: VirtualUtimeSpec, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.utimes_spec(path, atime, mtime, follow_symlinks) } fn truncate(&mut self, path: &str, length: u64) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = length; return Ok(()); } self.inner.truncate(path, length) } fn sync(&mut self, path: &str) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support sync: {path}"), )); } self.inner.sync(path) } fn allocate(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support allocation: {path}"), )); } self.inner.allocate(path, offset, length) } fn insert_range(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support insert range: {path}"), )); } self.inner.insert_range(path, offset, length) } fn collapse_range(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support collapse range: {path}"), )); } self.inner.collapse_range(path, offset, length) } fn zero_range( &mut self, path: &str, offset: u64, length: u64, keep_size: bool, ) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support zero range: {path}"), )); } self.inner.zero_range(path, offset, length, keep_size) } fn punch_hole(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support hole punching: {path}"), )); } self.inner.punch_hole(path, offset, length) } fn allocated_ranges(&mut self, path: &str) -> VfsResult<Vec<(u64, u64)>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support extent mapping: {path}"), )); } self.inner.allocated_ranges(path) } fn unwritten_ranges(&mut self, path: &str) -> VfsResult<Vec<(u64, u64)>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support extent mapping: {path}"), )); } self.inner.unwritten_ranges(path) } fn pread(&mut self, path: &str, offset: u64, length: usize) -> VfsResult<Vec<u8>> { if let Some(bytes) = read_stream_device(path, length) { return bytes; } if self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(Vec::new()); } self.inner.pread(path, offset, length) } fn pwrite(&mut self, path: &str, content: impl Into<Vec<u8>>, offset: u64) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = (content.into(), offset); return Ok(()); } self.inner.pwrite(path, content, offset) }} fn is_device_path(path: &str) -> bool { DEVICE_PATHS.contains(&path) || path.starts_with("/dev/fd/") || path.starts_with("/dev/pts/")} /// Standard emulated character devices (`/dev/null`, `/dev/zero`, `/dev/urandom`,/// `/dev/std{in,out,err}`). On Linux these are world-readable/writable and have no/// host backing — they are pure kernel emulations whose semantics this device layer/// already enforces (read/write for the stream devices, `EPERM` for unlink/rename)./// The permission layer therefore treats them as always accessible, so guest fs ops/// (`readFileSync`/`existsSync`/redirects on `/dev/null`, …) behave like native Linux/// regardless of the VM file-permission policy. Excludes `/dev/fd` and `/dev/pts`,/// which carry process-specific semantics the policy may legitimately govern.pub fn is_standard_device_path(path: &str) -> bool { DEVICE_PATHS.contains(&path)} fn is_sink_device_path(path: &str) -> bool { matches!( path, "/dev/null" | "/dev/zero" | "/dev/stdout" | "/dev/stderr" | "/dev/urandom" )} fn is_device_dir(path: &str) -> bool { path == "/dev" || DEVICE_DIRS.contains(&path)} Lines 1–500 of 602. Load more
use crate::vfs::{ VfsError, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat, VirtualUtimeSpec, }; use getrandom::getrandom; use web_time::{SystemTime, UNIX_EPOCH}; const DEVICE_PATHS: &[&str] = &[ "/dev/null", "/dev/zero", "/dev/stdin", "/dev/stdout", "/dev/stderr", "/dev/urandom", ]; const DEVICE_DIRS: &[&str] = &["/dev/fd", "/dev/pts"]; const DEFAULT_STREAM_DEVICE_READ_BYTES: usize = 4096; const DEV_DIR_ENTRIES: &[(&str, bool)] = &[ ("null", false), ("zero", false), ("stdin", false), ("stdout", false), ("stderr", false), ("urandom", false), ("fd", true), ]; #[derive(Debug, Clone)] pub struct DeviceLayer<V> { inner: V, } pub fn create_device_layer<V>(vfs: V) -> DeviceLayer<V> { DeviceLayer { inner: vfs } } impl<V> DeviceLayer<V> { pub fn into_inner(self) -> V { self.inner } pub fn inner(&self) -> &V { &self.inner } pub fn inner_mut(&mut self) -> &mut V { &mut self.inner } } impl<V: VirtualFileSystem> VirtualFileSystem for DeviceLayer<V> { fn read_file(&mut self, path: &str) -> VfsResult<Vec<u8>> { if let Some(bytes) = read_stream_device(path, DEFAULT_STREAM_DEVICE_READ_BYTES) { return bytes; } if self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(Vec::new()); } self.inner.read_file(path) } fn read_dir(&mut self, path: &str) -> VfsResult<Vec<String>> { if path == "/dev" { return Ok(DEV_DIR_ENTRIES .iter() .map(|(name, _)| String::from(*name)) .collect()); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir(path) } fn read_dir_limited(&mut self, path: &str, max_entries: usize) -> VfsResult<Vec<String>> { if path == "/dev" { let entries = DEV_DIR_ENTRIES .iter() .map(|(name, _)| String::from(*name)) .collect::<Vec<_>>(); if entries.len() > max_entries { return Err(VfsError::new( "ENOMEM", format!( "directory listing for '{path}' exceeds configured limit of {max_entries} entries" ), )); } return Ok(entries); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir_limited(path, max_entries) } fn read_dir_with_types(&mut self, path: &str) -> VfsResult<Vec<VirtualDirEntry>> { if path == "/dev" { return Ok(DEV_DIR_ENTRIES .iter() .map(|(name, is_directory)| VirtualDirEntry { name: String::from(*name), is_directory: *is_directory, is_symbolic_link: false, }) .collect()); } if DEVICE_DIRS.contains(&path) { return Ok(Vec::new()); } self.inner.read_dir_with_types(path) } fn write_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = content.into(); return Ok(()); } self.inner.write_file(path, content) } fn create_file_exclusive(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { let _ = content.into(); return Err(VfsError::new( "EEXIST", format!("file already exists, open '{path}'"), )); } self.inner.create_file_exclusive(path, content) } fn append_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<u64> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(content.into().len() as u64); } self.inner.append_file(path, content) } fn create_dir(&mut self, path: &str) -> VfsResult<()> { if is_device_dir(path) { return Ok(()); } self.inner.create_dir(path) } fn mkdir(&mut self, path: &str, recursive: bool) -> VfsResult<()> { if is_device_dir(path) { return Ok(()); } self.inner.mkdir(path, recursive) } fn mknod(&mut self, path: &str, mode: u32, rdev: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EEXIST", format!("device already exists: {path}"), )); } self.inner.mknod(path, mode, rdev) } fn exists(&self, path: &str) -> bool { if is_device_path(path) || is_device_dir(path) { return true; } self.inner.exists(path) } fn stat(&mut self, path: &str) -> VfsResult<VirtualStat> { if is_device_path(path) { return Ok(device_stat(path)); } if is_device_dir(path) { return Ok(device_dir_stat(path)); } self.inner.stat(path) } fn remove_file(&mut self, path: &str) -> VfsResult<()> { if is_device_path(path) { return Err(VfsError::permission_denied("unlink", path)); } self.inner.remove_file(path) } fn remove_dir(&mut self, path: &str) -> VfsResult<()> { if is_device_dir(path) { return Err(VfsError::permission_denied("rmdir", path)); } self.inner.remove_dir(path) } fn rename(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> { if is_device_path(old_path) || is_device_path(new_path) { return Err(VfsError::permission_denied("rename", old_path)); } self.inner.rename(old_path, new_path) } fn realpath(&self, path: &str) -> VfsResult<String> { if is_device_path(path) || is_device_dir(path) { return Ok(String::from(path)); } self.inner.realpath(path) } fn symlink(&mut self, target: &str, link_path: &str) -> VfsResult<()> { self.inner.symlink(target, link_path) } fn read_link(&self, path: &str) -> VfsResult<String> { self.inner.read_link(path) } fn lstat(&self, path: &str) -> VfsResult<VirtualStat> { if is_device_path(path) { return Ok(device_stat(path)); } if is_device_dir(path) { return Ok(device_dir_stat(path)); } self.inner.lstat(path) } fn link(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> { if is_device_path(old_path) { return Err(VfsError::permission_denied("link", old_path)); } self.inner.link(old_path, new_path) } fn chmod(&mut self, path: &str, mode: u32) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chmod(path, mode) } fn chown(&mut self, path: &str, uid: u32, gid: u32) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chown(path, uid, gid) } fn chown_spec( &mut self, path: &str, uid: u32, gid: u32, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.chown_spec(path, uid, gid, follow_symlinks) } fn get_xattr(&mut self, path: &str, name: &str, follow_symlinks: bool) -> VfsResult<Vec<u8>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.get_xattr(path, name, follow_symlinks) } fn list_xattrs(&mut self, path: &str, follow_symlinks: bool) -> VfsResult<Vec<String>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.list_xattrs(path, follow_symlinks) } fn set_xattr( &mut self, path: &str, name: &str, value: Vec<u8>, flags: u32, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner .set_xattr(path, name, value, flags, follow_symlinks) } fn remove_xattr(&mut self, path: &str, name: &str, follow_symlinks: bool) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EOPNOTSUPP", format!("extended attributes are not supported for device {path}"), )); } self.inner.remove_xattr(path, name, follow_symlinks) } fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.utimes(path, atime_ms, mtime_ms) } fn utimes_spec( &mut self, path: &str, atime: VirtualUtimeSpec, mtime: VirtualUtimeSpec, follow_symlinks: bool, ) -> VfsResult<()> { if is_device_path(path) { return Ok(()); } self.inner.utimes_spec(path, atime, mtime, follow_symlinks) } fn truncate(&mut self, path: &str, length: u64) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = length; return Ok(()); } self.inner.truncate(path, length) } fn sync(&mut self, path: &str) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support sync: {path}"), )); } self.inner.sync(path) } fn allocate(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support allocation: {path}"), )); } self.inner.allocate(path, offset, length) } fn insert_range(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support insert range: {path}"), )); } self.inner.insert_range(path, offset, length) } fn collapse_range(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support collapse range: {path}"), )); } self.inner.collapse_range(path, offset, length) } fn zero_range( &mut self, path: &str, offset: u64, length: u64, keep_size: bool, ) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support zero range: {path}"), )); } self.inner.zero_range(path, offset, length, keep_size) } fn punch_hole(&mut self, path: &str, offset: u64, length: u64) -> VfsResult<()> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support hole punching: {path}"), )); } self.inner.punch_hole(path, offset, length) } fn allocated_ranges(&mut self, path: &str) -> VfsResult<Vec<(u64, u64)>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support extent mapping: {path}"), )); } self.inner.allocated_ranges(path) } fn unwritten_ranges(&mut self, path: &str) -> VfsResult<Vec<(u64, u64)>> { if is_device_path(path) || is_device_dir(path) { return Err(VfsError::new( "EINVAL", format!("device does not support extent mapping: {path}"), )); } self.inner.unwritten_ranges(path) } fn pread(&mut self, path: &str, offset: u64, length: usize) -> VfsResult<Vec<u8>> { if let Some(bytes) = read_stream_device(path, length) { return bytes; } if self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { return Ok(Vec::new()); } self.inner.pread(path, offset, length) } fn pwrite(&mut self, path: &str, content: impl Into<Vec<u8>>, offset: u64) -> VfsResult<()> { if is_sink_device_path(path) || self .inner .stat(path) .is_ok_and(|stat| is_null_character_device(&stat)) { let _ = (content.into(), offset); return Ok(()); } self.inner.pwrite(path, content, offset) } } fn is_device_path(path: &str) -> bool { DEVICE_PATHS.contains(&path) || path.starts_with("/dev/fd/") || path.starts_with("/dev/pts/") } /// Standard emulated character devices (`/dev/null`, `/dev/zero`, `/dev/urandom`, /// `/dev/std{in,out,err}`). On Linux these are world-readable/writable and have no /// host backing — they are pure kernel emulations whose semantics this device layer /// already enforces (read/write for the stream devices, `EPERM` for unlink/rename). /// The permission layer therefore treats them as always accessible, so guest fs ops /// (`readFileSync`/`existsSync`/redirects on `/dev/null`, …) behave like native Linux /// regardless of the VM file-permission policy. Excludes `/dev/fd` and `/dev/pts`, /// which carry process-specific semantics the policy may legitimately govern. pub fn is_standard_device_path(path: &str) -> bool { DEVICE_PATHS.contains(&path) } fn is_sink_device_path(path: &str) -> bool { matches!( path, "/dev/null" | "/dev/zero" | "/dev/stdout" | "/dev/stderr" | "/dev/urandom" ) } fn is_device_dir(path: &str) -> bool { path == "/dev" || DEVICE_DIRS.contains(&path) }