use crate::fs::DeviceKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RootDevs {
None,
Minimal,
Standard,
}
#[derive(Debug, Clone)]
pub struct DeviceEntry {
pub name: String,
pub kind: DeviceKind,
pub major: u32,
pub minor: u32,
pub mode: u16,
}
pub fn device_table(kind: RootDevs) -> Vec<DeviceEntry> {
use DeviceKind::*;
let mut out: Vec<DeviceEntry> = Vec::new();
if matches!(kind, RootDevs::None) {
return out;
}
for (name, dk, major, minor, mode) in [
("console", Char, 5, 1, 0o600u16),
("null", Char, 1, 3, 0o666),
("zero", Char, 1, 5, 0o666),
("ptmx", Char, 5, 2, 0o666),
("tty", Char, 5, 0, 0o666),
("fuse", Char, 10, 229, 0o666),
("random", Char, 1, 8, 0o666),
("urandom", Char, 1, 9, 0o666),
] {
out.push(DeviceEntry {
name: name.to_string(),
kind: dk,
major,
minor,
mode,
});
}
if matches!(kind, RootDevs::Minimal) {
return out;
}
for i in 0..16u32 {
out.push(DeviceEntry {
name: format!("tty{i}"),
kind: Char,
major: 4,
minor: i,
mode: 0o620,
});
}
for i in 0..4u32 {
out.push(DeviceEntry {
name: format!("ttyS{i}"),
kind: Char,
major: 4,
minor: 64 + i,
mode: 0o660,
});
}
for (name, major, minor, mode) in [
("kmsg", 1u32, 11u32, 0o644u16),
("mem", 1, 1, 0o640),
("port", 1, 4, 0o640),
] {
out.push(DeviceEntry {
name: name.to_string(),
kind: Char,
major,
minor,
mode,
});
}
let ide_disks = [
("hda", 3u32, 0u32),
("hdb", 3, 64),
("hdc", 22, 0),
("hdd", 22, 64),
];
for (base, major, disk_minor) in ide_disks {
out.push(DeviceEntry {
name: base.to_string(),
kind: Block,
major,
minor: disk_minor,
mode: 0o660,
});
for p in 1..=4u32 {
out.push(DeviceEntry {
name: format!("{base}{p}"),
kind: Block,
major,
minor: disk_minor + p,
mode: 0o660,
});
}
}
for (i, base) in ["sda", "sdb", "sdc", "sdd"].iter().enumerate() {
let disk_minor = (i as u32) * 16;
out.push(DeviceEntry {
name: (*base).to_string(),
kind: Block,
major: 8,
minor: disk_minor,
mode: 0o660,
});
for p in 1..=4u32 {
out.push(DeviceEntry {
name: format!("{base}{p}"),
kind: Block,
major: 8,
minor: disk_minor + p,
mode: 0o660,
});
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn minimal_has_expected_entries() {
let t = device_table(RootDevs::Minimal);
let names: Vec<&str> = t.iter().map(|e| e.name.as_str()).collect();
assert_eq!(
names,
vec![
"console", "null", "zero", "ptmx", "tty", "fuse", "random", "urandom"
]
);
}
#[test]
fn standard_extends_minimal() {
let m: std::collections::HashSet<_> = device_table(RootDevs::Minimal)
.into_iter()
.map(|e| e.name)
.collect();
let s: std::collections::HashSet<_> = device_table(RootDevs::Standard)
.into_iter()
.map(|e| e.name)
.collect();
assert!(m.is_subset(&s));
assert!(s.contains("tty0"));
assert!(s.contains("tty15"));
assert!(!s.contains("tty16"));
assert!(s.contains("hda"));
assert!(s.contains("hda4"));
assert!(!s.contains("hda5"));
assert!(s.contains("sda"));
assert!(s.contains("sdd4"));
assert!(s.contains("kmsg"));
assert!(s.contains("mem"));
assert!(s.contains("port"));
}
#[test]
fn standard_entry_count_is_predictable() {
let s = device_table(RootDevs::Standard);
assert_eq!(s.len(), 71);
}
#[test]
fn standard_has_serial_ports() {
let s: std::collections::HashMap<String, (u32, u32)> = device_table(RootDevs::Standard)
.into_iter()
.map(|e| (e.name, (e.major, e.minor)))
.collect();
assert_eq!(s.get("ttyS0"), Some(&(4, 64)));
assert_eq!(s.get("ttyS3"), Some(&(4, 67)));
}
#[test]
fn none_is_empty() {
assert!(device_table(RootDevs::None).is_empty());
}
}