kanit

Toy init system
Log | Files | Refs | README | LICENSE

devfs.rs (6420B)


      1 use std::os::unix::fs::FileTypeExt;
      2 use std::path::Path;
      3 
      4 use async_fs::unix::symlink;
      5 use async_trait::async_trait;
      6 use blocking::unblock;
      7 use libc::dev_t;
      8 use log::info;
      9 use nix::mount::{MsFlags, mount};
     10 use nix::sys::stat::{Mode, SFlag, makedev, mknod};
     11 use nix::unistd::mkdir;
     12 
     13 use kanit_common::error::{Context, ErrorKind, Result, StaticError};
     14 use kanit_executor::join_all;
     15 use kanit_unit::{Dependencies, Unit};
     16 
     17 use crate::mounts::{
     18     MountAction, is_fs_available, is_fs_mounted, try_mount_from_fstab, try_mount_from_fstab_action,
     19 };
     20 use crate::oneshot::MDev;
     21 use crate::unit_name;
     22 
     23 pub struct DevFs;
     24 
     25 async fn character_device<P: AsRef<Path>>(path: P) -> bool {
     26     async_fs::metadata(path)
     27         .await
     28         .ok()
     29         .map(|m| m.file_type().is_char_device())
     30         .unwrap_or(false)
     31 }
     32 
     33 async fn exists<P: AsRef<Path>>(path: P) -> bool {
     34     async_fs::metadata(path).await.is_ok()
     35 }
     36 
     37 async fn mount_opt(
     38     path: &'static str,
     39     name: &'static str,
     40     mode: Mode,
     41     flags: MsFlags,
     42     opts: &'static str,
     43 ) -> Result<()> {
     44     if is_fs_available(name).await? && !is_fs_mounted(&path).await? {
     45         if !exists(path).await {
     46             unblock(move || mkdir(path, mode))
     47                 .await
     48                 .context("failed to make directory")?;
     49         }
     50 
     51         info!("mounting {path}");
     52 
     53         if try_mount_from_fstab(&path).await? {
     54             return Ok(());
     55         }
     56 
     57         unblock(move || {
     58             mount(
     59                 Some("none"),
     60                 path,
     61                 Some(name),
     62                 flags | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID,
     63                 Some(opts),
     64             )
     65         })
     66         .await
     67         .with_context(move || format!("failed to mount {name}"))?;
     68     }
     69 
     70     Ok(())
     71 }
     72 
     73 async fn dev_device(path: &'static str, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
     74     if character_device(path).await {
     75         return Ok(());
     76     }
     77 
     78     unblock(move || mknod(path, kind, perm, dev))
     79         .await
     80         .with_context(move || format!("failed to create {path}"))
     81 }
     82 
     83 async fn sym(src: &'static str, dst: &'static str) -> Result<()> {
     84     if !exists(dst).await {
     85         symlink(src, dst)
     86             .await
     87             .with_context(move || format!("failed to link {dst}"))?;
     88     }
     89 
     90     Ok(())
     91 }
     92 
     93 impl DevFs {
     94     async fn mount_dev() -> Result<()> {
     95         let path = Path::new("/dev");
     96 
     97         let mut opts = MsFlags::MS_NOSUID;
     98         let mut action = MountAction::Mount;
     99 
    100         if is_fs_mounted(path).await? {
    101             info!("remounting devfs");
    102             opts |= MsFlags::MS_REMOUNT;
    103             action = MountAction::Remount;
    104         } else {
    105             info!("mounting devfs");
    106         }
    107 
    108         if try_mount_from_fstab_action(path, action).await? {
    109             return Ok(());
    110         }
    111 
    112         let fs = if is_fs_available("devtmpfs").await? {
    113             "devtmpfs"
    114         } else if is_fs_available("tmpfs").await? {
    115             "tmpfs"
    116         } else {
    117             Err(StaticError(
    118                 "devtmpfs, tmpfs, nor fstab entry available, /dev will not be mounted",
    119             ))
    120             .kind(ErrorKind::Recoverable)?
    121         };
    122 
    123         unblock(move || mount(Some("none"), path, Some(fs), opts, Some("")))
    124             .await
    125             .context("failed to mount devfs")?;
    126 
    127         Ok(())
    128     }
    129 
    130     async fn populate_dev() -> Result<()> {
    131         // create dev devices if they don't exist
    132 
    133         join_all([
    134             dev_device(
    135                 "/dev/console",
    136                 SFlag::S_IFCHR,
    137                 Mode::S_IRUSR | Mode::S_IWUSR,
    138                 makedev(5, 1),
    139             ),
    140             dev_device(
    141                 "/dev/tty1",
    142                 SFlag::S_IFCHR,
    143                 Mode::S_IRUSR | Mode::S_IWUSR | Mode::S_IWGRP,
    144                 makedev(4, 1),
    145             ),
    146             dev_device(
    147                 "/dev/tty",
    148                 SFlag::S_IFCHR,
    149                 Mode::S_IRUSR
    150                     | Mode::S_IWUSR
    151                     | Mode::S_IRGRP
    152                     | Mode::S_IWGRP
    153                     | Mode::S_IROTH
    154                     | Mode::S_IWOTH,
    155                 makedev(4, 1),
    156             ),
    157             dev_device(
    158                 "/dev/null",
    159                 SFlag::S_IFCHR,
    160                 Mode::S_IRUSR
    161                     | Mode::S_IWUSR
    162                     | Mode::S_IRGRP
    163                     | Mode::S_IWGRP
    164                     | Mode::S_IROTH
    165                     | Mode::S_IWOTH,
    166                 makedev(1, 3),
    167             ),
    168             dev_device(
    169                 "/dev/kmsg",
    170                 SFlag::S_IFCHR,
    171                 Mode::S_IRUSR | Mode::S_IWUSR | Mode::S_IRGRP | Mode::S_IWGRP,
    172                 makedev(1, 11),
    173             ),
    174         ])
    175         .await
    176         .into_iter()
    177         .collect::<Result<Vec<_>>>()?;
    178 
    179         join_all([
    180             sym("/proc/self/fd", "/dev/fd"),
    181             sym("/proc/self/fd/0", "/dev/stdin"),
    182             sym("/proc/self/fd/1", "/dev/stdout"),
    183             sym("/proc/self/fd/2", "/dev/stderr"),
    184         ])
    185         .await
    186         .into_iter()
    187         .collect::<Result<Vec<_>>>()?;
    188 
    189         join_all([
    190             mount_opt(
    191                 "/dev/mqueue",
    192                 "mqueue",
    193                 Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO | Mode::S_ISVTX,
    194                 MsFlags::MS_NODEV,
    195                 "",
    196             ),
    197             mount_opt(
    198                 "/dev/pts",
    199                 "devpts",
    200                 Mode::S_IRWXU | Mode::S_IRGRP | Mode::S_IXGRP | Mode::S_IROTH | Mode::S_IXOTH,
    201                 MsFlags::empty(),
    202                 "gid=5,mode=0620",
    203             ),
    204             mount_opt(
    205                 "/dev/shm",
    206                 "tmpfs",
    207                 Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO | Mode::S_ISVTX,
    208                 MsFlags::MS_NODEV,
    209                 "",
    210             ),
    211         ])
    212         .await
    213         .into_iter()
    214         .collect::<Result<Vec<_>>>()?;
    215 
    216         if Path::new("/proc/kcore").exists() {
    217             symlink("/proc/kcore", "/dev/core")
    218                 .await
    219                 .context("failed to link kcore")?;
    220         }
    221 
    222         Ok(())
    223     }
    224 }
    225 
    226 #[async_trait]
    227 impl Unit for DevFs {
    228     unit_name!("devfs");
    229 
    230     fn dependencies(&self) -> Dependencies {
    231         Dependencies::new().before(MDev.name()).clone()
    232     }
    233 
    234     async fn start(&mut self) -> Result<()> {
    235         Self::mount_dev().await?;
    236         Self::populate_dev().await?;
    237 
    238         Ok(())
    239     }
    240 }