Repository: pop-os/popsicle (language: rust)

Issue:
Writing image bigger than USB
<!--

If this is a bug, please use the template below. If this is a question or general discussion topic, please start a conversation in our chat https://chat.pop-os.org/ or post on our subreddit https://reddit.com/r/pop_os - as those are the proper forums for that type of discussion.

-->


**Distribution (run `cat /etc/os-release`):**
 NAME="Pop!_OS"
VERSION="18.04 LTS"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Pop!_OS 18.04 LTS"
VERSION_ID="18.04"
HOME_URL="https://system76.com/pop"
SUPPORT_URL="http://support.system76.com"
BUG_REPORT_URL="https://github.com/pop-os/pop/issues"
PRIVACY_POLICY_URL="https://system76.com/privacy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
 
 
**Related Application and/or Package Version (run `apt policy $PACKAGE NAME`):**
popsicle:
  Installed: 0.1.5~1526005323~18.04~9254724
  Candidate: 0.1.5~1526005323~18.04~9254724
  Version table:
 *** 0.1.5~1526005323~18.04~9254724 1001
       1001 http://ppa.launchpad.net/system76/pop/ubuntu bionic/main amd64 Packages
        100 /var/lib/dpkg/status


**Issue/Bug Description:**
Popsicle allows writing a bigger image than the capacity of the USB without warning the user.


**Expected behavior:**
A prompt should appear saying that this operation cannot be done since the size of the image is bigger than the USB.




Discussion / hints:
Rather than a prompt, I'll simply make the devices unselectable.
With a hint would be good as well (Device too small).

Fix patch:
diff --git a/gtk/src/app/state/device_selection.rs b/gtk/src/app/state/device_selection.rs
index bcb6814..4ae40e6 100644
--- a/gtk/src/app/state/device_selection.rs
+++ b/gtk/src/app/state/device_selection.rs
@@ -37,6 +37,7 @@ macro_rules! try_or_error {
 /// Move to device selection screen
 pub fn initialize(
     state: &State,
+    all: &gtk::CheckButton,
     back: &gtk::Button,
     error: &gtk::Label,
     list: &gtk::ListBox,
@@ -64,12 +65,13 @@ pub fn initialize(
         eprintln!("popsicle: unable to get devices: {}", why);
     }
 
-    refresh_device_list(state, &devices, back, error, list, next, stack);
+    refresh_device_list(state, &devices, all, back, error, list, next, stack);
 }
 
 pub fn refresh_device_list(
     state: &State,
     devices: &[String],
+    all: &gtk::CheckButton,
     back: &gtk::Button,
     error: &gtk::Label,
     list: &gtk::ListBox,
@@ -90,7 +92,8 @@ pub fn refresh_device_list(
     device_list.clear();
 
     list.get_children().iter().for_each(|c| c.destroy());
-
+    let image_sectors = (state.image_length.get() / 512 + 1) as u64;
+    let mut all_is_sensitive = false;
     for device in devices {
         // Attempt to get the canonical path of the device.
         // Display the error view if this fails.
@@ -106,12 +109,24 @@ pub fn refresh_device_list(
         );
 
         let button = if let Some(block) = BlockDevice::new(&name) {
-            CheckButton::new_with_label(&[
-                &block.label(),
-                " (",
-                &name.to_string_lossy(),
-                ")",
-            ].concat())
+            let too_small = block.sectors() < image_sectors;
+
+            let button = CheckButton::new_with_label(&{
+                if too_small {
+                    [ &block.label(), " (", &name.to_string_lossy(), "): Device is too small" ].concat()
+                } else {
+                    [ &block.label(), " (", &name.to_string_lossy(), ")" ].concat()
+                }
+            });
+
+            if too_small {
+                button.set_tooltip_text("Device is too small");
+                button.set_has_tooltip(true);
+                button.set_sensitive(false);
+            } else {
+                all_is_sensitive = true;
+            }
+            button
         } else {
             CheckButton::new_with_label(&name.to_string_lossy())
         };
@@ -121,6 +136,7 @@ pub fn refresh_device_list(
     }
 
     list.show_all();
+    all.set_sensitive(all_is_sensitive);
 }
 
 pub fn device_requires_refresh(
diff --git a/gtk/src/app/state/mod.rs b/gtk/src/app/state/mod.rs
index 549b63c..344e6fd 100644
--- a/gtk/src/app/state/mod.rs
+++ b/gtk/src/app/state/mod.rs
@@ -252,7 +252,7 @@ impl Connect for App {
                 if state.view.get() == 1 {
                     match device_selection::device_requires_refresh(&state, &back, &error, &next, &stack) {
                         Some(devices) => {
-                            device_selection::refresh_device_list(&state, &devices, &back, &error, &list, &next, &stack);
+                            device_selection::refresh_device_list(&state, &devices, &all, &back, &error, &list, &next, &stack);
                             all.set_active(false);
                             next.set_sensitive(false);
                         }
@@ -275,7 +275,7 @@ impl Connect for App {
             stack.set_transition_type(StackTransitionType::SlideLeft);
 
             match view_value {
-                0 => device_selection::initialize(&state, &back, &error, &list, &next, &stack),
+                0 => device_selection::initialize(&state, &all, &back, &error, &list, &next, &stack),
                 1 => flash_devices(&state, &back, &error, &next, &stack, &summary_grid),
                 2 => gtk::main_quit(),
                 _ => unreachable!(),
@@ -318,7 +318,7 @@ impl Connect for App {
 
             devices
                 .iter()
-                .for_each(|&(_, ref device)| device.set_active(all.get_active()));
+                .for_each(|&(_, ref device)| device.set_active(all.get_active() && device.is_sensitive()));
         });
     }
 
diff --git a/gtk/src/block.rs b/gtk/src/block.rs
index 6cdcdf1..b66e620 100644
--- a/gtk/src/block.rs
+++ b/gtk/src/block.rs
@@ -2,6 +2,11 @@ use std::default::Default;
 use std::fs::File;
 use std::io::Read;
 use std::path::{Path, PathBuf};
+use std::thread::sleep;
+use std::time::Duration;
+
+const SLEEP_AFTER_FAIL: u64 = 500;
+const ATTEMPTS: u64 = 10_000 / SLEEP_AFTER_FAIL;
 
 fn read_file(path: &Path) -> String {
     File::open(path)
@@ -29,6 +34,19 @@ impl BlockDevice {
         })
     }
 
+    pub fn sectors(&self) -> u64 {
+        let get_sectors = || read_file(&self.path.join("size")).parse::<u64>().unwrap_or(0);
+        let (mut result, mut attempts) = (get_sectors(), 0);
+
+        while result == 0 || attempts == ATTEMPTS {
+            result = get_sectors();
+            sleep(Duration::from_millis(SLEEP_AFTER_FAIL));
+            attempts += 1;
+        }
+
+        result
+    }
+
     pub fn vendor(&self) -> String { read_file(&self.path.join("device/vendor")) }
 
     pub fn model(&self) -> String { read_file(&self.path.join("device/model")) }


Test patch: