node_launchpad/
system.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use color_eyre::eyre::eyre;
10use color_eyre::eyre::ContextCompat;
11use color_eyre::Result;
12use faccess::{AccessMode, PathExt};
13
14use std::env;
15
16use std::path::Path;
17use std::path::PathBuf;
18use std::process::Command;
19use sysinfo::Disks;
20
21// Tries to get the default (drive name, mount point) of the current executable
22// to be used as the default drive
23pub fn get_default_mount_point() -> Result<(String, PathBuf)> {
24    // Create a new System instance
25    let disks = Disks::new_with_refreshed_list();
26
27    // Get the current executable path
28    let exe_path = env::current_exe()?;
29
30    // Iterate over the disks and find the one that matches the executable path
31    for disk in disks.list() {
32        if exe_path.starts_with(disk.mount_point()) {
33            return Ok((
34                disk.name().to_string_lossy().into(),
35                disk.mount_point().to_path_buf(),
36            ));
37        }
38    }
39    Err(eyre!("Cannot find the default mount point"))
40}
41
42// Checks if the given path has read and write access
43fn has_read_write_access(path: PathBuf) -> bool {
44    let check_access = |mode, access_type| match path.access(mode) {
45        Ok(_) => {
46            debug!("{} access granted for {:?}", access_type, path);
47            true
48        }
49        Err(_) => {
50            debug!("{} access denied for {:?}", access_type, path);
51            false
52        }
53    };
54
55    let read = check_access(AccessMode::READ, "Read");
56    let write = check_access(AccessMode::WRITE, "Write");
57
58    read && write
59}
60
61/// Gets a list of available drives, their available space and if it's accessible.
62///
63/// An accessible drive is a drive that is readable and writable.
64///
65pub fn get_list_of_available_drives_and_available_space(
66) -> Result<Vec<(String, PathBuf, u64, bool)>> {
67    let disks = Disks::new_with_refreshed_list();
68    let mut drives: Vec<(String, PathBuf, u64, bool)> = Vec::new();
69
70    let default_mountpoint = match get_default_mount_point() {
71        Ok((_name, mountpoint)) => mountpoint,
72        Err(_) => PathBuf::new(),
73    };
74
75    for disk in disks.list() {
76        let disk_info = (
77            disk.name()
78                .to_string_lossy()
79                .into_owned()
80                .trim()
81                .to_string(),
82            disk.mount_point().to_path_buf(),
83            disk.available_space(),
84            has_read_write_access(disk.mount_point().to_path_buf())
85                || default_mountpoint == disk.mount_point().to_path_buf(),
86        );
87
88        // We avoid adding the same disk multiple times if it's mounted in multiple places
89        // We check the name and free space to determine if it's the same disk
90        if !drives
91            .iter()
92            .any(|drive| drive.0 == disk_info.0 && drive.2 == disk_info.2)
93        {
94            debug!("[ADD] Disk added: {:?}", disk_info);
95            drives.push(disk_info);
96        } else {
97            debug!("[SKIP] Disk {:?} already added before.", disk_info);
98        }
99    }
100
101    debug!("Drives detected: {:?}", drives);
102    Ok(drives)
103}
104
105// Opens a folder in the file explorer
106pub fn open_folder(path: &str) -> std::io::Result<()> {
107    if Path::new(path).exists() {
108        #[cfg(target_os = "macos")]
109        Command::new("open").arg(path).spawn()?.wait()?;
110        #[cfg(target_os = "windows")]
111        Command::new("explorer").arg(path).spawn()?.wait()?;
112        #[cfg(target_os = "linux")]
113        Command::new("xdg-open").arg(path).spawn()?.wait()?;
114    } else {
115        error!("Path does not exist: {}", path);
116    }
117    Ok(())
118}
119
120#[cfg(unix)]
121pub fn get_primary_mount_point() -> PathBuf {
122    PathBuf::from("/")
123}
124#[cfg(windows)]
125pub fn get_primary_mount_point() -> PathBuf {
126    PathBuf::from("C:\\")
127}
128
129/// Gets the name of the primary mount point.
130pub fn get_primary_mount_point_name() -> Result<String> {
131    let primary_mount_point = get_primary_mount_point();
132    let available_drives = get_list_of_available_drives_and_available_space()?;
133
134    available_drives
135        .iter()
136        .find(|(_, mount_point, _, _)| mount_point == &primary_mount_point)
137        .map(|(name, _, _, _)| name.clone())
138        .ok_or_else(|| eyre!("Unable to find the name of the primary mount point"))
139}
140
141// Gets available disk space in bytes for the given mountpoint
142pub fn get_available_space_b(storage_mountpoint: &PathBuf) -> Result<usize> {
143    let disks = Disks::new_with_refreshed_list();
144    if tracing::level_enabled!(tracing::Level::DEBUG) {
145        for disk in disks.list() {
146            let res = disk.mount_point() == storage_mountpoint;
147            debug!(
148                "Disk: {disk:?} is equal to '{:?}': {res:?}",
149                storage_mountpoint,
150            );
151        }
152    }
153
154    let available_space_b = disks
155        .list()
156        .iter()
157        .find(|disk| disk.mount_point() == storage_mountpoint)
158        .context("Cannot find the primary disk. Configuration file might be wrong.")?
159        .available_space() as usize;
160
161    Ok(available_space_b)
162}
163
164// Gets the name of the drive given a mountpoint
165pub fn get_drive_name(storage_mountpoint: &PathBuf) -> Result<String> {
166    let disks = Disks::new_with_refreshed_list();
167    let name = disks
168        .list()
169        .iter()
170        .find(|disk| disk.mount_point() == storage_mountpoint)
171        .context("Cannot find the primary disk. Configuration file might be wrong.")?
172        .name()
173        .to_str()
174        .unwrap_or_default()
175        .to_string();
176
177    Ok(name)
178}