Feature: Eingerichtetes Laufwerkspaar gehoert automatisch dem einrichtenden sudo-Nutzer

'smart-mount drive add' setzt 'owner_user' jetzt standardmaessig auf den
Nutzer, in dessen Namen 'sudo' aufgerufen wurde ($SUDO_USER) - wer ein Paar
einrichtet, bekommt also ohne weiteres Zutun vollen Zugriff darauf (uid/gid).
Ueberschreibbar per '--owner-user <name>' (nicht-interaktiv) oder im
interaktiven Prompt, der jetzt auch den konkreten Nutzernamen nennt und dort
abgelehnt oder auf einen anderen Namen geaendert werden kann. 'drive edit'
bleibt unveraendert (Default bleibt der bisherige Owner).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 13:32:45 +02:00
co-authored by Claude Sonnet 5
parent c6526e2130
commit f0c0a69b9c
2 changed files with 34 additions and 9 deletions
+6 -2
View File
@@ -202,8 +202,12 @@ Eintragen ausgegeben:
- **Voller Zugriff für einen bestimmten Nutzer (`owner_user`)**: Bei CIFS/WebDAV (keine
nativen Unix-Rechte) setzt SmartMount automatisch `uid=`/`gid=`/`file_mode=0700`/
`dir_mode=0700`, sobald ein Paar ein `owner_user` hat - optional, ohne `owner_user` gehört
der Mount root. Bei NFS gibt es keine clientseitige `uid=`/`gid=`-Option, Zugriff bestimmt
dort ausschließlich der Server über die tatsächlichen Datei-Eigentümer/-Rechte des Exports.
der Mount root. `smart-mount drive add` setzt `owner_user` standardmäßig auf den Nutzer, der
`sudo` aufgerufen hat (`$SUDO_USER`) - wer ein Paar einrichtet, bekommt also automatisch
vollen Zugriff darauf, sofern nicht per `--owner-user` (nicht-interaktiv) oder im
interaktiven Prompt bewusst ein anderer Nutzer angegeben bzw. abgelehnt wird. Bei NFS gibt es
keine clientseitige `uid=`/`gid=`-Option, Zugriff bestimmt dort ausschließlich der Server über
die tatsächlichen Datei-Eigentümer/-Rechte des Exports.
- **davfs2-Konfiguration**: SmartMount setzt in `davfs2.conf` automatisch `gui_optimize 1`
(bündelt PROPFIND-Anfragen, wichtig für grafische Dateimanager) sowie `buf_size 16384`
(deutlich über dem Standard von 16 KiB) - Letzteres behebt ein bekanntes Praxisproblem, bei
+28 -7
View File
@@ -151,7 +151,10 @@ async fn add(args: DriveArgs) -> anyhow::Result<()> {
let ni = args.non_interactive;
let name = resolve_field(args.name, None, "Drive pair name", ni, true)?.expect("required");
let owner_user = resolve_owner_user(args.owner_user, None, ni)?;
// Der Nutzer, der das Paar einrichtet, bekommt standardmäßig vollen Zugriff darauf (siehe
// `invoking_user`) - überschreibbar per '--owner-user' oder (interaktiv) durch Ablehnen/
// einen anderen Namen im Prompt.
let owner_user = resolve_owner_user(args.owner_user, invoking_user().as_deref(), ni)?;
let local_kind = resolve_kind(args.local_kind, None, "Local mount type", ni)?;
let local_address = resolve_local_address(args.local_ip, args.local_mac, None, ni)?;
@@ -391,29 +394,47 @@ fn resolve_field(
Ok(Some(input.interact_text()?))
}
/// Der Nutzer, in dessen Namen `sudo` aufgerufen wurde (falls so aufgerufen) - Standardwert für
/// `owner_user` beim Anlegen eines neuen Paares (siehe `add`), damit der einrichtende Nutzer
/// ohne extra Angabe vollen Zugriff auf sein eigenes Laufwerkspaar bekommt. `None`, wenn direkt
/// als root angemeldet (kein "einrichtender Nutzer" außer root selbst) oder `SUDO_USER` leer/
/// nicht gesetzt ist.
fn invoking_user() -> Option<String> {
std::env::var("SUDO_USER").ok().filter(|u| !u.is_empty())
}
/// Optional: falls gesetzt, bekommt dieser Nutzer bei CIFS/WebDAV vollen Zugriff
/// (uid=/gid=/file_mode=0700/dir_mode=0700) statt der sonst üblichen root-Ownership - der
/// Mount selbst läuft immer als root (siehe [`smart_mount::systemd`]).
/// Mount selbst läuft immer als root (siehe [`smart_mount::systemd`]). `default_owner` ist der
/// vorbelegte/vorgeschlagene Wert (bei `add`: der einrichtende Nutzer, siehe `invoking_user`;
/// bei `edit`: der bisherige `owner_user`) - immer überschreibbar per Flag, im interaktiven
/// Prompt auch durch Ablehnen oder einen anderen Namen.
fn resolve_owner_user(
flag: Option<String>,
current: Option<&str>,
default_owner: Option<&str>,
non_interactive: bool,
) -> anyhow::Result<Option<String>> {
if let Some(v) = flag {
return Ok(Some(v));
}
if non_interactive {
return Ok(current.map(str::to_string));
return Ok(default_owner.map(str::to_string));
}
let prompt = match default_owner {
Some(user) => format!(
"Should '{user}' get full access to this drive (uid/gid, including script execution)?"
),
None => "Should a specific user get full access to this drive (uid/gid, including script execution)?".to_string(),
};
let want_owner = Confirm::new()
.with_prompt("Should a specific user get full access to this drive (uid/gid, including script execution)?")
.default(current.is_some())
.with_prompt(prompt)
.default(default_owner.is_some())
.interact()?;
if !want_owner {
return Ok(None);
}
let mut input = Input::<String>::new().with_prompt("Linux username");
if let Some(c) = current {
if let Some(c) = default_owner {
input = input.default(c.to_string());
}
Ok(Some(input.interact_text()?))