Clarify the `DeviceId` encoding
This commit is contained in:
parent
a13297ae4c
commit
d73cbb5008
|
|
@ -64,12 +64,7 @@ pub fn init() -> Result<()> {
|
|||
// Instead of hardcoding every device numbers in this function,
|
||||
// a registration mechanism should be used to allow each driver to
|
||||
// allocate device IDs either statically or dynamically.
|
||||
pub fn get_device(dev: usize) -> Result<Arc<dyn Device>> {
|
||||
if dev == 0 {
|
||||
return_errno_with_message!(Errno::EPERM, "whiteout device")
|
||||
}
|
||||
|
||||
let devid = DeviceId::from(dev as u64);
|
||||
pub fn get_device(devid: DeviceId) -> Result<Arc<dyn Device>> {
|
||||
let major = devid.major();
|
||||
let minor = devid.minor();
|
||||
|
||||
|
|
@ -79,6 +74,6 @@ pub fn get_device(dev: usize) -> Result<Arc<dyn Device>> {
|
|||
(5, 0) => Ok(Arc::new(tty::TtyDevice)),
|
||||
(1, 8) => Ok(Arc::new(random::Random)),
|
||||
(1, 9) => Ok(Arc::new(urandom::Urandom)),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "unsupported device"),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "the device ID is invalid or unsupported"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,17 +63,32 @@ impl DeviceId {
|
|||
pub fn minor(&self) -> u32 {
|
||||
self.minor
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the device ID as a `u32` value.
|
||||
impl DeviceId {
|
||||
/// Creates a device ID from the encoded `u64` value.
|
||||
///
|
||||
/// The encoding strategy here is the same as in Linux. See the Linux implementation at:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/kdev_t.h#L39-L44>
|
||||
pub fn as_encoded_u32(&self) -> u32 {
|
||||
self.as_encoded_u64() as u32
|
||||
/// See [`as_encoded_u64`] for details about how to encode a device ID to a `u64` value.
|
||||
///
|
||||
/// [`as_encoded_u64`]: Self::as_encoded_u64
|
||||
pub fn from_encoded_u64(raw: u64) -> Self {
|
||||
let major = ((raw >> 32) & 0xffff_f000 | (raw >> 8) & 0x0000_0fff) as u32;
|
||||
let minor = ((raw >> 12) & 0xffff_ff00 | raw & 0x0000_00ff) as u32;
|
||||
Self::new(major, minor)
|
||||
}
|
||||
|
||||
/// Encodes the device ID as a `u64` value.
|
||||
fn as_encoded_u64(&self) -> u64 {
|
||||
///
|
||||
/// The lower 32 bits use the same encoding strategy as Linux. See the Linux implementation at:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/kdev_t.h#L39-L44>.
|
||||
///
|
||||
/// If the major or minor device number is too large, the additional bits will be recorded
|
||||
/// using the higher 32 bits. Note that as of 2025, the Linux kernel still has no support for
|
||||
/// 64-bit device IDs:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/types.h#L18>.
|
||||
/// So this encoding follows the implementation in glibc:
|
||||
/// <https://github.com/bminor/glibc/blob/632d895f3e5d98162f77b9c3c1da4ec19968b671/bits/sysmacros.h#L26-L34>.
|
||||
pub fn as_encoded_u64(&self) -> u64 {
|
||||
let major = self.major() as u64;
|
||||
let minor = self.minor() as u64;
|
||||
((major & 0xffff_f000) << 32)
|
||||
|
|
@ -81,25 +96,6 @@ impl DeviceId {
|
|||
| ((minor & 0xffff_ff00) << 12)
|
||||
| (minor & 0x0000_00ff)
|
||||
}
|
||||
|
||||
/// Decodes the device ID from a `u64` value.
|
||||
fn decode_from_u64(raw: u64) -> Self {
|
||||
let major = ((raw >> 32) & 0xffff_f000 | (raw >> 8) & 0x0000_0fff) as u32;
|
||||
let minor = ((raw >> 12) & 0xffff_ff00 | raw & 0x0000_00ff) as u32;
|
||||
Self::new(major, minor)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceId> for u64 {
|
||||
fn from(value: DeviceId) -> Self {
|
||||
value.as_encoded_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for DeviceId {
|
||||
fn from(raw: u64) -> Self {
|
||||
Self::decode_from_u64(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a device node to FS for the device.
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ impl Inode for Ext2Inode {
|
|||
let inode = match type_ {
|
||||
MknodType::CharDeviceNode(dev) | MknodType::BlockDeviceNode(dev) => {
|
||||
let inode = self.create(name, inode_type, mode.into())?;
|
||||
inode.set_device_id(dev.id().into()).unwrap();
|
||||
inode.set_device_id(dev.id().as_encoded_u64()).unwrap();
|
||||
inode
|
||||
}
|
||||
_ => todo!(),
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ impl FileOps for StatFileOps {
|
|||
|
||||
let (tty_nr, tpgid) = if let Some(terminal) = process.terminal() {
|
||||
(
|
||||
terminal.id().as_encoded_u32(),
|
||||
terminal.id().as_encoded_u64(),
|
||||
terminal
|
||||
.job_control()
|
||||
.foreground()
|
||||
|
|
|
|||
|
|
@ -1109,7 +1109,7 @@ impl Inode for RamInode {
|
|||
let rdev = self
|
||||
.inner
|
||||
.as_device()
|
||||
.map(|device| device.id().into())
|
||||
.map(|device| device.id().as_encoded_u64())
|
||||
.unwrap_or(0);
|
||||
let inode_metadata = self.metadata.lock();
|
||||
Metadata {
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ impl Metadata {
|
|||
nlinks: 1,
|
||||
uid: Uid::new_root(),
|
||||
gid: Gid::new_root(),
|
||||
rdev: device.id().into(),
|
||||
rdev: device.id().as_encoded_u64(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use super::SyscallReturn;
|
|||
use crate::{
|
||||
device::get_device,
|
||||
fs::{
|
||||
device::DeviceId,
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
utils::{InodeMode, InodeType, MknodType},
|
||||
|
|
@ -49,7 +50,7 @@ pub fn sys_mknodat(
|
|||
let _ = dir_dentry.new_fs_child(&name, InodeType::File, inode_mode)?;
|
||||
}
|
||||
InodeType::CharDevice | InodeType::BlockDevice => {
|
||||
let device_inode = get_device(dev)?;
|
||||
let device_inode = get_device(DeviceId::from_encoded_u64(dev as u64))?;
|
||||
let _ = dir_dentry.mknod(&name, inode_mode, device_inode.into())?;
|
||||
}
|
||||
InodeType::NamedPipe => {
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ pub struct Statx {
|
|||
|
||||
impl From<Metadata> for Statx {
|
||||
fn from(info: Metadata) -> Self {
|
||||
let devid = DeviceId::from(info.dev);
|
||||
let rdevid = DeviceId::from(info.rdev);
|
||||
let devid = DeviceId::from_encoded_u64(info.dev);
|
||||
let rdevid = DeviceId::from_encoded_u64(info.rdev);
|
||||
|
||||
// FIXME: We assume it is always not mount_root.
|
||||
let stx_attributes = 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
MknodTest.MknodAtFIFO
|
||||
MknodTest.MknodOnExistingPathFails
|
||||
# Broken support or no support at all.
|
||||
MknodTest.Socket
|
||||
MknodTest.Fifo
|
||||
MknodTest.FifoOtrunc
|
||||
MknodTest.FifoTruncNoOp
|
||||
MknodTest.FifoTruncNoOp
|
||||
|
||||
# Buggy or unimplemented in exfat and ext2.
|
||||
MknodTest.MknodAtFIFO
|
||||
MknodTest.MknodOnExistingPathFails
|
||||
|
||||
# This test cannot pass in Linux when run as root, and
|
||||
# it has been removed from the latest version of gVisor.
|
||||
MknodTest.UnimplementedTypesReturnError
|
||||
|
|
|
|||
Loading…
Reference in New Issue