Bugfix/disable uasp (#878)

* usbstoraged

* set quirks on demand

* remove reference to usbstoraged.service

* comments

* use sysfs instead of modprobe and address comments

* fix ro fs and missing comma

* Apply suggestions from code review

* don't recurse overlayroot

* sleep before reconnect

* reset twice

* sync between writes

* fix build

* fix build

* adds drive to storage quirk whitelist

* long sleep

* another thing to try

* fix: Ensure that the changes take effect

Co-authored-by: Keagan McClelland <keagan.mcclelland@gmail.com>
Co-authored-by: Justin Miller <dragondef@gmail.com>
This commit is contained in:
Aiden McClelland
2021-12-04 20:51:41 -07:00
parent e968a1cca7
commit a71d07f353
7 changed files with 173 additions and 5 deletions

View File

@@ -14,8 +14,11 @@ use crate::util::{display_serializable, IoFormat, Version};
use crate::Error;
pub mod main;
pub mod quirks;
pub mod util;
pub const BOOT_RW_PATH: &'static str = "/media/boot-rw";
#[command(subcommands(list, backup_info))]
pub fn disk() -> Result<(), Error> {
Ok(())

155
appmgr/src/disk/quirks.rs Normal file
View File

@@ -0,0 +1,155 @@
use std::num::ParseIntError;
use std::path::Path;
use std::time::Duration;
use color_eyre::eyre::eyre;
use tokio::io::AsyncWriteExt;
use tracing::instrument;
use super::BOOT_RW_PATH;
use crate::util::AtomicFile;
use crate::Error;
pub const QUIRK_PATH: &'static str = "/sys/module/usb_storage/parameters/quirks";
pub const WHITELIST: [(VendorId, ProductId); 4] = [
(VendorId(0x1d6b), ProductId(0x0002)), // root hub usb2
(VendorId(0x1d6b), ProductId(0x0003)), // root hub usb3
(VendorId(0x2109), ProductId(0x3431)),
(VendorId(0x1058), ProductId(0x262f)), // western digital black HDD
];
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct VendorId(u16);
impl std::str::FromStr for VendorId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u16::from_str_radix(s.trim(), 16).map(VendorId)
}
}
impl std::fmt::Display for VendorId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ProductId(u16);
impl std::str::FromStr for ProductId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u16::from_str_radix(s.trim(), 16).map(ProductId)
}
}
impl std::fmt::Display for ProductId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct Quirks(Vec<(VendorId, ProductId)>);
impl Quirks {
pub fn add(&mut self, vendor: VendorId, product: ProductId) {
self.0.push((vendor, product));
}
pub fn contains(&self, vendor: VendorId, product: ProductId) -> bool {
self.0.contains(&(vendor, product))
}
}
impl std::fmt::Display for Quirks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut comma = false;
for (vendor, product) in &self.0 {
if comma {
write!(f, ",")?;
} else {
comma = true;
}
write!(f, "{}:{}:u", vendor, product)?;
}
Ok(())
}
}
impl std::str::FromStr for Quirks {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let mut quirks = Vec::new();
for item in s.split(",") {
if let [vendor, product, "u"] = item.splitn(3, ":").collect::<Vec<_>>().as_slice() {
quirks.push((vendor.parse()?, product.parse()?));
} else {
return Err(Error::new(
eyre!("Invalid quirk: `{}`", item),
crate::ErrorKind::DiskManagement,
));
}
}
Ok(Quirks(quirks))
}
}
#[instrument]
pub async fn update_quirks(quirks: &mut Quirks) -> Result<(), Error> {
let mut usb_devices = tokio::fs::read_dir("/sys/bus/usb/devices/").await?;
while let Some(usb_device) = usb_devices.next_entry().await? {
if tokio::fs::metadata(usb_device.path().join("idVendor"))
.await
.is_err()
{
continue;
}
let vendor = tokio::fs::read_to_string(usb_device.path().join("idVendor"))
.await?
.parse()?;
let product = tokio::fs::read_to_string(usb_device.path().join("idProduct"))
.await?
.parse()?;
if WHITELIST.contains(&(vendor, product)) || quirks.contains(vendor, product) {
continue;
}
quirks.add(vendor, product);
tokio::fs::write(QUIRK_PATH, quirks.to_string()).await?;
reconnect_usb(usb_device.path()).await?;
}
Ok(())
}
#[instrument(skip(usb_device_path))]
pub async fn reconnect_usb(usb_device_path: impl AsRef<Path>) -> Result<(), Error> {
let authorized_path = usb_device_path.as_ref().join("authorized");
let mut authorized_file = tokio::fs::File::create(&authorized_path).await?;
authorized_file.write_all(b"0").await?;
authorized_file.sync_all().await?;
drop(authorized_file);
tokio::time::sleep(Duration::from_secs(1)).await;
let mut authorized_file = tokio::fs::File::create(&authorized_path).await?;
authorized_file.write_all(b"1").await?;
authorized_file.sync_all().await?;
tokio::time::sleep(Duration::from_secs(1)).await;
Ok(())
}
#[instrument]
pub async fn fetch_quirks() -> Result<Quirks, Error> {
Ok(tokio::fs::read_to_string(QUIRK_PATH).await?.parse()?)
}
#[instrument]
pub async fn save_quirks(quirks: &Quirks) -> Result<(), Error> {
let orig_path = Path::new(BOOT_RW_PATH).join("cmdline.txt.orig");
let target_path = Path::new(BOOT_RW_PATH).join("cmdline.txt");
if tokio::fs::metadata(&orig_path).await.is_err() {
tokio::fs::copy(&target_path, &orig_path).await?;
}
let cmdline = tokio::fs::read_to_string(&orig_path).await?;
let mut target = AtomicFile::new(&target_path).await?;
target
.write_all(format!("usb-storage.quirks={} {}", quirks, cmdline).as_bytes())
.await?;
target.save().await?;
Ok(())
}

View File

@@ -22,6 +22,7 @@ use tokio::process::Command;
use tokio::sync::Mutex;
use tracing::instrument;
use super::quirks::{fetch_quirks, save_quirks, update_quirks};
use super::BackupInfo;
use crate::auth::check_password;
use crate::middleware::encrypt::{decrypt_slice, encrypt_slice};
@@ -206,6 +207,9 @@ pub async fn pvscan() -> Result<BTreeMap<PathBuf, Option<String>>, Error> {
#[instrument]
pub async fn list() -> Result<Vec<DiskInfo>, Error> {
let mut quirks = fetch_quirks().await?;
update_quirks(&mut quirks).await?;
save_quirks(&mut quirks).await?;
let disk_guids = pvscan().await?;
let disks = tokio_stream::wrappers::ReadDirStream::new(
tokio::fs::read_dir(DISK_PATH)

3
build/fstab Normal file
View File

@@ -0,0 +1,3 @@
LABEL=green / ext4 discard,errors=remount-ro 0 1
LABEL=system-boot /media/boot-rw vfat defaults 0 1
/media/boot-rw /boot/firmware none defaults,bind,ro 0 0

View File

@@ -47,7 +47,7 @@ ControlPort 9051
CookieAuthentication 1
EOF
echo 'overlayroot="tmpfs"' > /etc/overlayroot.local.conf
echo 'overlayroot="tmpfs":swap=1,recurse=0' > /etc/overlayroot.local.conf
systemctl disable initialization.service
sync
reboot

View File

@@ -21,8 +21,11 @@ sudo e2label ${OUTPUT_DEVICE}p4 blue
mkdir -p /tmp/eos-mnt
sudo mount ${OUTPUT_DEVICE}p1 /tmp/eos-mnt
sudo sed -i 's/^/usb-storage.quirks=152d:0562:u /g' /tmp/eos-mnt/cmdline.txt
sudo sed -i 's/LABEL=writable/LABEL=green/g' /tmp/eos-mnt/cmdline.txt
# create a copy of the cmdline *without* the quirk string, so that it can be easily amended
sudo cp /tmp/eos-mnt/cmdline.txt /tmp/eos-mnt/cmdline.txt.orig
sudo sed -i 's/^/usb-storage.quirks=152d:0562:u /g' /tmp/eos-mnt/cmdline.txt
cat /tmp/eos-mnt/config.txt | grep -v "dtoverlay=" | sudo tee /tmp/eos-mnt/config.txt.tmp
echo "dtoverlay=pwm-2chan" | sudo tee -a /tmp/eos-mnt/config.txt.tmp
sudo mv /tmp/eos-mnt/config.txt.tmp /tmp/eos-mnt/config.txt
@@ -35,8 +38,8 @@ sudo umount /tmp/eos-mnt
sudo mount ${OUTPUT_DEVICE}p3 /tmp/eos-mnt
sudo sed -i 's/LABEL=writable/LABEL=green/g' /tmp/eos-mnt/etc/fstab
sudo sed -i 's/LABEL=system-boot\(\s\+\S\+\s\+\S\+\s\+\)defaults/LABEL=system-boot\1defaults,ro/g' /tmp/eos-mnt/etc/fstab
sudo mkdir /tmp/eos-mnt/media/boot-rw
sudo cp build/fstab /tmp/eos-mnt/etc/fstab
# Enter the appmgr directory, copy over the built EmbassyOS binaries and systemd services, edit the nginx config, then create the .ssh directory
cd appmgr/