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 }