Fix: Hinzufügen von Locking-Mechanismen und verbessertes Mount-/Unmount-Handling
- Globaler RW-Lock eingeführt, um parallele Statusabfragen und Mount-/Unmount-Operationen zu synchronisieren. - Funktionsspezifische Mutexe für Mount und Unmount hinzugefügt, um konsistente Lock-Reihenfolgen zu gewährleisten. - Verbesserte Fehlerbehandlung beim Mounten, inklusive Umgang mit „already mounted“ und „busy“ Fehlern. - Refactoring zur Nutzung von `/proc/mounts` statt `mount`-Befehl auf Nicht-Windows-Systemen. - Normalisierung von Mount-Pfaden implementiert.
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
use crate::log::{log, LogLevel};
|
||||
use crate::filesystem::mounted::{/* is_mounted, */ FS_MOUNT_GUARD};
|
||||
use crate::log::{LogLevel, log};
|
||||
use std::process::Command;
|
||||
use std::sync::{Mutex, OnceLock, RwLock};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
static GUARD_MOUNT: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
static GUARD_UNMOUNT: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
|
||||
/// Führt die Einbindung eines Dateisystems durch.
|
||||
///
|
||||
@@ -8,6 +15,16 @@ use std::process::Command;
|
||||
/// * `network_path` - Der Netzwerkpfad zum einzubindenden Dateisystem
|
||||
/// * `mount_type` - Der Mount-Typ, z.B. "nfs" oder "davfs2"
|
||||
pub fn mount(mount_point: &str, network_path: &str, mount_type: &str) {
|
||||
// 1) Globalen Write-Lock halten (blockiert parallele Statusabfragen)
|
||||
let _fs_write = FS_MOUNT_GUARD
|
||||
.get_or_init(|| RwLock::new(()))
|
||||
.write()
|
||||
.expect("mount rwlock poisoned");
|
||||
|
||||
// 2) Danach funktionsspezifischen Mutex sperren (konsistente Lock-Reihenfolge!)
|
||||
let m = GUARD_MOUNT.get_or_init(|| Mutex::new(()));
|
||||
let _lock = m.lock().expect("Mutex poisoned");
|
||||
|
||||
log(
|
||||
"mount",
|
||||
&*format!(
|
||||
@@ -53,12 +70,7 @@ pub fn mount(mount_point: &str, network_path: &str, mount_type: &str) {
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let mount_point: &str = if mount_point.ends_with('/') {
|
||||
mount_point
|
||||
} else {
|
||||
&*format!("{}/", mount_point)
|
||||
};
|
||||
|
||||
// Verzeichnis vorbereiten
|
||||
let output = Command::new("mkdir")
|
||||
.arg("-p")
|
||||
.arg(mount_point)
|
||||
@@ -103,9 +115,28 @@ pub fn mount(mount_point: &str, network_path: &str, mount_type: &str) {
|
||||
.output()
|
||||
.expect("Failed to execute mount command");
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
if output.status.success() {
|
||||
log("mount", "Filesystem mounted successfully.", LogLevel::Info);
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
// Häufige „schon gemountet“/Busy-Indikatoren tolerant behandeln
|
||||
let already_or_busy = stderr.contains("already mounted")
|
||||
|| stderr.contains("is busy")
|
||||
|| stderr.contains("Device or resource busy")
|
||||
|| stderr.contains("EBUSY");
|
||||
|
||||
if already_or_busy {
|
||||
log(
|
||||
"mount",
|
||||
"Filesystem appears to be already mounted or mountpoint is busy. Treating as no-op.",
|
||||
LogLevel::Info,
|
||||
);
|
||||
log("mount", &*stderr, LogLevel::Debug);
|
||||
return;
|
||||
}
|
||||
|
||||
log(
|
||||
"mount",
|
||||
&*format!(
|
||||
@@ -114,11 +145,7 @@ pub fn mount(mount_point: &str, network_path: &str, mount_type: &str) {
|
||||
),
|
||||
LogLevel::Error,
|
||||
);
|
||||
log(
|
||||
"mount",
|
||||
&*String::from_utf8_lossy(&output.stderr),
|
||||
LogLevel::Debug,
|
||||
);
|
||||
log("mount", &*stderr, LogLevel::Debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +155,16 @@ pub fn mount(mount_point: &str, network_path: &str, mount_type: &str) {
|
||||
/// # Parameter
|
||||
/// * `mount_point` - Der lokale Verzeichnispfad, von dem das Dateisystem ausgehängt werden soll
|
||||
pub fn unmount(mount_point: &str) {
|
||||
// 1) Globalen Write-Lock halten (blockiert parallele Statusabfragen)
|
||||
let _fs_write = FS_MOUNT_GUARD
|
||||
.get_or_init(|| RwLock::new(()))
|
||||
.write()
|
||||
.expect("mount rwlock poisoned");
|
||||
|
||||
// 2) Danach funktionsspezifischen Mutex sperren (konsistente Lock-Reihenfolge!)
|
||||
let m = GUARD_UNMOUNT.get_or_init(|| Mutex::new(()));
|
||||
let _lock = m.lock().expect("Mutex poisoned");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let output = Command::new("Remove-PSDrive")
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
use std::process::Command;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::Path;
|
||||
use std::sync::{OnceLock, RwLock};
|
||||
|
||||
// Globaler RW-Lock für Mount-Operationen und Statusabfragen
|
||||
pub static FS_MOUNT_GUARD: OnceLock<RwLock<()>> = OnceLock::new();
|
||||
|
||||
fn guard() -> &'static RwLock<()> {
|
||||
FS_MOUNT_GUARD.get_or_init(|| RwLock::new(()))
|
||||
}
|
||||
|
||||
/// Überprüft, ob ein Dateisystem am angegebenen Mount-Point mit dem spezifizierten Mount-Typ eingebunden ist.
|
||||
///
|
||||
@@ -10,13 +20,20 @@ use std::process::Command;
|
||||
/// * `true` wenn das Dateisystem mit dem angegebenen Typ eingebunden ist
|
||||
/// * `false` wenn das Dateisystem nicht oder mit einem anderen Typ eingebunden ist
|
||||
pub fn is_mounted_as(mount_point: &str, mount_type: &str) -> bool {
|
||||
// Während Statusabfragen nur Read-Lock halten
|
||||
let _read_guard = guard().read().expect("mount rwlock poisoned");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::process::Command;
|
||||
let drive_letter = &mount_point[0..1];
|
||||
let output = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!("(Get-PSDrive -Name {} -PSProvider 'FileSystem').Description", drive_letter)
|
||||
&format!(
|
||||
"(Get-PSDrive -Name {} -PSProvider 'FileSystem').Description",
|
||||
drive_letter
|
||||
),
|
||||
])
|
||||
.output()
|
||||
.expect("Failed to execute get-psdrive command");
|
||||
@@ -31,19 +48,18 @@ pub fn is_mounted_as(mount_point: &str, mount_type: &str) -> bool {
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let output = Command::new("mount")
|
||||
.output()
|
||||
.expect("Failed to execute mount command");
|
||||
let norm_mp = normalize_mount_point(mount_point);
|
||||
|
||||
if !output.status.success() {
|
||||
return false;
|
||||
for entry in read_proc_mounts() {
|
||||
let (mp, fstype, _opts, _src) = entry;
|
||||
if mp == norm_mp {
|
||||
if type_matches(mount_type, &fstype, &_opts) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mount_output = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
mount_output
|
||||
.lines()
|
||||
.any(|line| line.contains(mount_point) && line.contains(mount_type))
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +72,20 @@ pub fn is_mounted_as(mount_point: &str, mount_type: &str) -> bool {
|
||||
/// * `true` wenn ein Dateisystem am angegebenen Pfad eingebunden ist
|
||||
/// * `false` wenn kein Dateisystem eingebunden ist
|
||||
pub fn is_mounted(mount_point: &str) -> bool {
|
||||
// Während Statusabfragen nur Read-Lock halten
|
||||
let _read_guard = guard().read().expect("mount rwlock poisoned");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::process::Command;
|
||||
let drive_letter = &mount_point[0..1];
|
||||
let output = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!("(Get-PSDrive -Name {} -PSProvider 'FileSystem')", drive_letter)
|
||||
&format!(
|
||||
"(Get-PSDrive -Name {} -PSProvider 'FileSystem')",
|
||||
drive_letter
|
||||
),
|
||||
])
|
||||
.output()
|
||||
.expect("Failed to execute get-psdrive command");
|
||||
@@ -72,16 +95,73 @@ pub fn is_mounted(mount_point: &str) -> bool {
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let output = Command::new("mount")
|
||||
.output()
|
||||
.expect("Failed to execute mount command");
|
||||
|
||||
if !output.status.success() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mount_output = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
mount_output.lines().any(|line| line.contains(mount_point))
|
||||
let norm_mp = normalize_mount_point(mount_point);
|
||||
read_proc_mounts()
|
||||
.into_iter()
|
||||
.any(|(mp, _, _, _)| mp == norm_mp)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn read_proc_mounts() -> Vec<(String, String, String, String)> {
|
||||
// Liefert Tupel: (mount_point, fstype, options, source)
|
||||
let file = match File::open("/proc/mounts") {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
let reader = BufReader::new(file);
|
||||
let mut result = Vec::new();
|
||||
|
||||
for line in reader.lines().flatten() {
|
||||
// Format /proc/mounts:
|
||||
// fs_spec fs_file fs_vfstype fs_mntops fs_freq fs_passno
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() < 6 {
|
||||
continue;
|
||||
}
|
||||
let fs_spec = parts[0].to_string();
|
||||
let fs_file = parts[1].to_string();
|
||||
let fs_vfstype = parts[2].to_string();
|
||||
let fs_mntops = parts[3].to_string();
|
||||
|
||||
result.push((fs_file, fs_vfstype, fs_mntops, fs_spec));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn normalize_mount_point(p: &str) -> String {
|
||||
// Entfernt redundante Slashes am Ende (außer bei "/") und canonicalized soweit möglich.
|
||||
if p == "/" {
|
||||
return "/".to_string();
|
||||
}
|
||||
let trimmed = p.trim_end_matches('/');
|
||||
// Versuche, realpath zu bilden, falle sonst auf trimmed zurück
|
||||
let path = Path::new(trimmed);
|
||||
path.canonicalize()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|_| trimmed.to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn type_matches(expected: &str, actual_fstype: &str, options: &str) -> bool {
|
||||
// Normalisiere erwartete Typfamilien
|
||||
match expected {
|
||||
// NFS kann als nfs oder nfs4 erscheinen
|
||||
"nfs" | "nfs4" => actual_fstype == "nfs" || actual_fstype == "nfs4",
|
||||
|
||||
// davfs/webdav erscheint häufig als fuse.davfs (oder fuse mit helper=davfs)
|
||||
"webdav" | "davfs" | "davfs2" => {
|
||||
actual_fstype == "fuse.davfs"
|
||||
|| actual_fstype == "davfs"
|
||||
|| (actual_fstype == "fuse" && options.contains("helper=davfs"))
|
||||
}
|
||||
|
||||
// CIFS/Samba Alias
|
||||
"cifs" | "smb" | "smb3" => actual_fstype == "cifs" || actual_fstype == "smb3",
|
||||
|
||||
// Fallback: exakter Vergleich
|
||||
other => other == actual_fstype,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user