diff --git a/Cargo.toml b/Cargo.toml index 8e26a05..910200d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,9 @@ gtk4 = { version = "0.6.6", features = [ "v4_10", ] } libusb = "0.3.0" -nix = "0.26.2" +nix = { version = "0.26.2", features = [ + "fs" +] } phf = "0.11.1" phf_macros = "0.11.1" relm4 = { version = "0.6.0", features = [ diff --git a/src/file_utils.rs b/src/file_utils.rs index 2cc6fc6..9e2caa3 100644 --- a/src/file_utils.rs +++ b/src/file_utils.rs @@ -1,6 +1,6 @@ -use crate::{constants::CMD_NAME, runner::Runner}; +use crate::runner::Runner; +use nix::{sys::statvfs::{statvfs, FsFlags}, errno::Errno}; use std::{ - env, fs::{self, copy, create_dir_all, remove_dir_all, File, OpenOptions}, io::{BufReader, BufWriter}, path::Path, @@ -93,6 +93,25 @@ pub fn copy_file(source_s: &String, dest_s: &String) { ); } } - set_file_readonly(dest_s, false); + set_file_readonly(dest_s, false) + .expect(format!("Failed to set file {} as rw", dest_s).as_str()); copy(source, dest).expect(format!("Failed to copy {} to {}", source_s, dest_s).as_str()); } + +pub fn mount_has_nosuid(path_s: &str) -> Result { + let path = Path::new(path_s); + match statvfs(path) { + Ok(fstats) => Ok(fstats.flags().contains(FsFlags::ST_NOSUID)), + Err(e) => Err(e) + } +} + +#[cfg(test)] +mod tests { + use crate::file_utils::mount_has_nosuid; + + #[test] + fn can_get_nosuid() { + assert_eq!(mount_has_nosuid("/tmp").expect("Error running statvfs"), true); + } +} diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index d2d1f14..9ecb981 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -5,6 +5,7 @@ use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg}; use crate::config::Config; use crate::constants::APP_NAME; +use crate::file_utils::mount_has_nosuid; use crate::profile::Profile; use crate::ui::app::{ AboutAction, BuildProfileAction, BuildProfileCleanAction, DebugViewToggleAction, @@ -181,6 +182,59 @@ impl SimpleComponent for MainView { }, }, model.devices_box.widget(), + gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_hexpand: true, + set_vexpand: false, + set_spacing: 12, + set_margin_top: 12, + set_margin_bottom: 12, + #[track = "model.changed(Self::selected_profile())"] + set_visible: match mount_has_nosuid(model.selected_profile.prefix.as_str()) { + Ok(b) => b, + Err(_) => { + // TODO: handle this error better + println!( + "Warning: could not get stat on path {}", + model.selected_profile.prefix); + false + }, + }, + gtk::Separator { + set_orientation: gtk::Orientation::Horizontal, + set_hexpand: true, + }, + gtk::Box { + set_orientation: gtk::Orientation::Horizontal, + set_margin_start: 12, + set_margin_end: 12, + set_spacing: 12, + set_hexpand: true, + gtk::Image { + set_icon_name: Some("dialog-warning-symbolic"), + add_css_class: "warning", + }, + gtk::Label { + add_css_class: "warning", + add_css_class: "heading", + set_label: "Warning" + } + }, + gtk::Label { + set_label: concat!( + "Your current prefix is inside a partition ", + "mounted with the nosuid option. This will prevent ", + "the Rex2 runtime from acquiring certain privileges ", + "and will cause noticeable stutter when running XR ", + "applications." + ), + set_margin_start: 12, + set_margin_end: 12, + add_css_class: "warning", + set_wrap: true, + set_wrap_mode: gtk::pango::WrapMode::Word, + } + }, model.steam_launch_options_box.widget(), model.install_wivrn_box.widget(), gtk::Box {