|
1 | 1 | use std::ffi::CStr;
|
2 | 2 |
|
3 |
| -use std::mem::zeroed; |
| 3 | +use std::mem::MaybeUninit; |
4 | 4 |
|
5 |
| -use crate::common::Error; |
| 5 | +use crate::{common::Error, cutils::cerr}; |
6 | 6 |
|
7 |
| -pub fn kernel_check(major: u32, minor: u32) -> Result<(), Error> { |
8 |
| - if cfg!(target_os = "freebsd") { |
9 |
| - return Ok(()); |
10 |
| - } |
| 7 | +#[cfg(target_os = "linux")] |
| 8 | +pub fn kernel_check() -> Result<(), Error> { |
| 9 | + // On Linux, we need kernel version 5.9 to have access to `close_range()` |
| 10 | + const TARGET_VERSION: (u32, u32) = (5, 9); |
11 | 11 |
|
12 |
| - let mut utsname: libc::utsname = unsafe { zeroed() }; |
| 12 | + let mut utsname = MaybeUninit::uninit(); |
13 | 13 |
|
14 |
| - if unsafe { libc::uname(&mut utsname) } != 0 { |
15 |
| - // Could not get the kernel version. Try to run anyway |
16 |
| - return Ok(()); |
17 |
| - } |
| 14 | + // SAFETY: uname is passed a correct pointer |
| 15 | + cerr(unsafe { libc::uname(utsname.as_mut_ptr()) })?; |
18 | 16 |
|
19 |
| - let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) } |
20 |
| - .to_string_lossy() |
21 |
| - .into_owned(); |
| 17 | + // SAFETY: since uname exited normally, the struct is now initialized |
| 18 | + let utsname = unsafe { utsname.assume_init() }; |
22 | 19 |
|
23 |
| - let version_parts: Vec<&str> = release.split('.').collect(); |
24 |
| - |
25 |
| - if version_parts.len() < 2 { |
26 |
| - // Could not get the kernel version. Try to run anyway |
27 |
| - return Ok(()); |
28 |
| - } |
| 20 | + // SAFETY: utsname.release will hold a null-terminated C string |
| 21 | + let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) }.to_string_lossy(); |
29 | 22 |
|
30 | 23 | // Parse the major and minor version numbers
|
31 |
| - if let (Ok(major_version), Ok(minor_version)) = ( |
32 |
| - version_parts[0].parse::<u32>(), |
33 |
| - version_parts[1].parse::<u32>(), |
34 |
| - ) { |
35 |
| - if major_version > major || (major_version == major && minor_version >= minor) { |
36 |
| - return Ok(()); |
| 24 | + let mut version_parts = release.split('.').map_while(|x| x.parse::<u32>().ok()); |
| 25 | + |
| 26 | + match (version_parts.next(), version_parts.next()) { |
| 27 | + (Some(major), Some(minor)) if (major, minor) < TARGET_VERSION => { |
| 28 | + // We have determined that this Linux kernel is too old. |
| 29 | + Err(Error::KernelCheck) |
| 30 | + } |
| 31 | + _ => { |
| 32 | + // We have not been able to prove that sudo-rs is incompatible with this kernel |
| 33 | + // and are giving the benefit of the doubt. |
| 34 | + Ok(()) |
37 | 35 | }
|
38 | 36 | }
|
| 37 | +} |
| 38 | + |
| 39 | +#[cfg(target_os = "freebsd")] |
| 40 | +pub fn kernel_check() -> Result<(), Error> { |
| 41 | + // the kernel check doesn't make much sense on FreeBSD (we need FreeBSD 8.0 or newer, |
| 42 | + // which is comparatively ancient compared to Linux 5.9) |
| 43 | + Ok(()) |
| 44 | +} |
39 | 45 |
|
40 |
| - Err(Error::KernelCheck) |
| 46 | +#[cfg(not(any(target_os = "freebsd", target_os = "linux")))] |
| 47 | +pub fn kernel_check() -> Result<(), Error> { |
| 48 | + compile_error!("sudo-rs only works on Linux and FreeBSD"); |
41 | 49 | }
|
0 commit comments