summaryrefslogtreecommitdiffstats
path: root/src/iso.rs
blob: ae0f07d7d31f999aa63aa88f0aca6c6c4a52b1c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use anyhow::{anyhow, Result};
use log::info;

use crate::{
    mount_help::MountHelper,
    password::{execute_sudo_command, execute_sudo_command_with_password, PasswordHolder},
};

/// Used to mount an iso file as a loop device.
/// Holds info about its source (`path`) and optional mountpoint (`mountpoints`).
/// Since it's used once and nothing can be done with it after mounting, it's dropped as soon as possible.
#[derive(Debug, Clone, Default)]
pub struct IsoDevice {
    /// The source, aka the iso file itself.
    pub path: String,
    /// None when creating, updated once the device is mounted.
    pub mountpoints: Option<std::path::PathBuf>,
    is_mounted: bool,
}

impl IsoDevice {
    const FILENAME: &str = "fm_iso";

    /// Creates a new instance from an iso file path.
    pub fn from_path(path: String) -> Self {
        log::info!("IsoDevice from_path: {path}");
        Self {
            path,
            ..Default::default()
        }
    }

    fn mountpoints(&self, username: &str) -> std::path::PathBuf {
        let mut mountpoint = std::path::PathBuf::from("/run/media");
        mountpoint.push(username);
        mountpoint.push(Self::FILENAME);
        mountpoint
    }
}

impl MountHelper for IsoDevice {
    fn format_mkdir_parameters(&self, username: &str) -> [String; 3] {
        [
            "mkdir".to_owned(),
            "-p".to_owned(),
            format!("/run/media/{}/{}", username, Self::FILENAME),
        ]
    }

    fn format_mount_parameters(&mut self, username: &str) -> Vec<String> {
        let mountpoints = self.mountpoints(username);
        self.mountpoints = Some(mountpoints.clone());
        vec![
            "mount".to_owned(),
            "-o".to_owned(),
            "loop".to_owned(),
            self.path.clone(),
            mountpoints.to_string_lossy().to_string(),
        ]
    }

    fn format_umount_parameters(&self, username: &str) -> Vec<String> {
        vec![
            "umount".to_owned(),
            format!(
                "/run/media/{}/{}",
                username,
                self.mountpoints(username).display(),
            ),
        ]
    }

    fn is_mounted(&self) -> bool {
        self.is_mounted
    }

    fn umount(&mut self, username: &str, passwords: &mut PasswordHolder) -> Result<bool> {
        let root_path = std::path::Path::new("/");
        // sudo
        let (success, _, _) = execute_sudo_command_with_password(
            &["-S", "ls", "/root"],
            &passwords.sudo()?,
            root_path,
        )?;
        if !success {
            return Ok(false);
        }
        // unmount
        let (success, stdout, stderr) =
            execute_sudo_command(&self.format_umount_parameters(username))?;
        info!("stdout: {}\nstderr: {}", stdout, stderr);
        if success {
            self.is_mounted = false;
        }
        // sudo -k
        let (success, stdout, stderr) = execute_sudo_command(&["-k"])?;
        info!("stdout: {}\nstderr: {}", stdout, stderr);
        Ok(success)
    }

    fn mount(&mut self, username: &str, passwords: &mut PasswordHolder) -> Result<bool> {
        let root_path = std::path::Path::new("/");
        info!("iso mount: {username}, {passwords:?}");
        if self.is_mounted {
            Err(anyhow!("iso device mount: device is already mounted"))
        } else {
            // sudo
            let (success, _, _) = execute_sudo_command_with_password(
                &["ls", "/root"],
                &passwords.sudo()?,
                root_path,
            )?;
            if !success {
                return Ok(false);
            }
            // mkdir
            let (success, stdout, stderr) =
                execute_sudo_command(&self.format_mkdir_parameters(username))?;
            info!("stdout: {}\nstderr: {}", stdout, stderr);
            let mut last_success = false;
            if success {
                // mount
                let (success, stdout, stderr) =
                    execute_sudo_command(&self.format_mount_parameters(username))?;
                last_success = success;
                info!("stdout: {}\nstderr: {}", stdout, stderr);
                // sudo -k
                self.is_mounted = success;
            } else {
                self.is_mounted = false;
            }
            execute_sudo_command(&["-k"])?;
            Ok(last_success)
        }
    }

    /// String representation of the device.
    fn as_string(&self) -> Result<String> {
        if let Some(ref mount_point) = self.mountpoints {
            Ok(format!(
                "mounted {} to {}",
                self.path,
                mount_point.display()
            ))
        } else {
            Ok(format!("not mounted {}", self.path))
        }
    }

    fn device_name(&self) -> Result<String> {
        Ok(self.path.to_owned())
    }
}