Add portable mode with removable media detection and scanning

This commit is contained in:
lashman
2026-02-28 00:16:52 +02:00
parent f2abfba753
commit 2108b0f3d8
8 changed files with 138 additions and 1 deletions

View File

@@ -1786,6 +1786,14 @@ impl Database {
Ok(())
}
pub fn set_portable(&self, id: i64, portable: bool, mount_point: Option<&str>) -> SqlResult<()> {
self.conn.execute(
"UPDATE appimages SET is_portable = ?2, mount_point = ?3 WHERE id = ?1",
params![id, portable as i32, mount_point],
)?;
Ok(())
}
// --- Launch statistics ---
pub fn get_top_launched(&self, limit: i32) -> SqlResult<Vec<(String, u64)>> {

View File

@@ -11,6 +11,7 @@ pub mod integrator;
pub mod launcher;
pub mod notification;
pub mod orphan;
pub mod portable;
pub mod report;
pub mod security;
pub mod updater;

71
src/core/portable.rs Normal file
View File

@@ -0,0 +1,71 @@
use std::path::{Path, PathBuf};
/// Information about a mounted removable device.
#[derive(Debug, Clone)]
pub struct MountInfo {
pub device: String,
pub mount_point: PathBuf,
pub fs_type: String,
}
/// Detect removable media mount points by parsing /proc/mounts.
/// Looks for user-accessible removable paths with common removable fs types.
pub fn detect_removable_mounts() -> Vec<MountInfo> {
let content = match std::fs::read_to_string("/proc/mounts") {
Ok(c) => c,
Err(_) => return Vec::new(),
};
let removable_prefixes = ["/media/", "/run/media/", "/mnt/"];
let removable_fs = ["vfat", "exfat", "ntfs", "ntfs3", "fuseblk", "ext4", "ext3", "btrfs"];
content
.lines()
.filter_map(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 3 {
return None;
}
let device = parts[0];
let mount_point = parts[1];
let fs_type = parts[2];
// Check if mount point is in a removable location
let is_removable = removable_prefixes
.iter()
.any(|prefix| mount_point.starts_with(prefix));
if !is_removable {
return None;
}
// Check filesystem type
if !removable_fs.contains(&fs_type) {
return None;
}
Some(MountInfo {
device: device.to_string(),
mount_point: PathBuf::from(mount_point),
fs_type: fs_type.to_string(),
})
})
.collect()
}
/// Check if a given path is located on a removable device.
pub fn is_path_on_removable(path: &Path) -> bool {
let mounts = detect_removable_mounts();
mounts.iter().any(|m| path.starts_with(&m.mount_point))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_path_on_removable_with_no_mounts() {
// On a test system, /home paths shouldn't be on removable media
assert!(!is_path_on_removable(Path::new("/home/user/test.AppImage")));
}
}