fix(cloud-files): Pin/Unpin tatsaechlich wirksam machen + CLI-Logging
set_pin_state hatte drei Probleme: - FILE_READ_ATTRIBUTES: CfSetPinState braucht WRITE_ATTRIBUTES - Kein OPEN_REPARSE_POINT: das Oeffnen selbst hat evtl. die Hydration getriggert, bevor wir unpinnen konnten - Kein CfDehydratePlaceholder: Pin-Wechsel auf UNPINNED aendert nur das Flag, der Disk-Space wird nicht freigegeben Jetzt: - WRITE_ATTRIBUTES + OPEN_REPARSE_POINT beim Handle-Oeffnen - Bei Unpin zusaetzlich CfDehydratePlaceholder, damit "Speicher freigeben" auch wirklich Platz freiraeumt - Ergebnis + Fehler werden nach <parent>\.minicloud-cloudfiles.log geschrieben, damit wir sehen was passiert handle_cli_shortcuts loggt nun nach %LOCALAPPDATA%\MiniCloud Sync\ cli.log, weil Explorer die stdout/stderr eines gestarteten Prozesses verwirft. Ohne das Log kann man die vom Kontextmenue gestarteten Aktionen nicht debuggen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
85dae4377f
commit
3c340f9653
|
|
@ -509,20 +509,24 @@ fn create_placeholder(
|
||||||
|
|
||||||
pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
|
pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
|
||||||
use windows::Win32::Storage::FileSystem::{
|
use windows::Win32::Storage::FileSystem::{
|
||||||
CreateFileW, FILE_FLAG_BACKUP_SEMANTICS, FILE_READ_ATTRIBUTES,
|
CreateFileW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
|
FILE_WRITE_ATTRIBUTES, FILE_READ_ATTRIBUTES,
|
||||||
|
FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE, OPEN_EXISTING,
|
||||||
};
|
};
|
||||||
|
|
||||||
let path_wide = U16CString::from_str(file.to_string_lossy().as_ref())
|
let path_wide = U16CString::from_str(file.to_string_lossy().as_ref())
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
// CfSetPinState / CfDehydratePlaceholder brauchen WRITE_ATTRIBUTES.
|
||||||
|
// OPEN_REPARSE_POINT verhindert, dass das Oeffnen selbst eine
|
||||||
|
// Hydration ausloest (sonst waere Unpin bedeutungslos).
|
||||||
let handle = unsafe {
|
let handle = unsafe {
|
||||||
CreateFileW(
|
CreateFileW(
|
||||||
PCWSTR(path_wide.as_ptr()),
|
PCWSTR(path_wide.as_ptr()),
|
||||||
FILE_READ_ATTRIBUTES.0,
|
(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES).0,
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
None,
|
None,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_FLAG_BACKUP_SEMANTICS,
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -533,12 +537,50 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
|
||||||
} else {
|
} else {
|
||||||
CF::CF_PIN_STATE_UNPINNED
|
CF::CF_PIN_STATE_UNPINNED
|
||||||
};
|
};
|
||||||
let res = unsafe {
|
let set_res = unsafe {
|
||||||
CF::CfSetPinState(handle, state, CF::CF_SET_PIN_FLAG_NONE, None)
|
CF::CfSetPinState(handle, state, CF::CF_SET_PIN_FLAG_NONE, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// "Speicher freigeben" (unpin): sofort dehydrieren, damit der Platz
|
||||||
|
// wirklich frei wird. CfSetPinState allein aendert nur den Zustand.
|
||||||
|
// Der Rueckgabewert wird geloggt aber nicht hart als Fehler gewertet,
|
||||||
|
// weil die Datei evtl. schon dehydriert ist.
|
||||||
|
let dehydrate_err = if !pinned && set_res.is_ok() {
|
||||||
|
let r = unsafe {
|
||||||
|
CF::CfDehydratePlaceholder(
|
||||||
|
handle,
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
CF::CF_DEHYDRATE_FLAG_NONE,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
r.err().map(|e| format!("{:?}", e))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = windows::Win32::Foundation::CloseHandle(handle);
|
let _ = windows::Win32::Foundation::CloseHandle(handle);
|
||||||
}
|
}
|
||||||
res.map_err(|e| format!("CfSetPinState: {e}"))?;
|
|
||||||
|
// Log-Verzeichnis ist der Mount-Ordner oder dessen Parent
|
||||||
|
let log_dir = file
|
||||||
|
.ancestors()
|
||||||
|
.find(|p| p.parent().is_some())
|
||||||
|
.map(|p| p.to_path_buf())
|
||||||
|
.unwrap_or_else(|| file.to_path_buf());
|
||||||
|
log_msg(
|
||||||
|
&log_dir,
|
||||||
|
&format!(
|
||||||
|
"set_pin_state file={} pinned={} result={:?} dehydrate_err={:?}",
|
||||||
|
file.display(),
|
||||||
|
pinned,
|
||||||
|
set_res,
|
||||||
|
dehydrate_err
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
set_res.map_err(|e| format!("CfSetPinState: {e}"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1102,24 +1102,49 @@ async fn fetch_remote_entries(
|
||||||
/// Short-circuit fuer Shell-Kontextmenue-Aufrufe:
|
/// Short-circuit fuer Shell-Kontextmenue-Aufrufe:
|
||||||
/// `minicloud-sync --pin <file>` oder `--unpin <file>` fuehrt die
|
/// `minicloud-sync --pin <file>` oder `--unpin <file>` fuehrt die
|
||||||
/// Aktion direkt aus und beendet. Kein UI, kein Tray.
|
/// Aktion direkt aus und beendet. Kein UI, kein Tray.
|
||||||
|
/// Logs landen in %LOCALAPPDATA%\MiniCloud Sync\cli.log - sonst
|
||||||
|
/// wuerden wir vom Explorer gestartete Prozesse nie debuggen koennen.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn handle_cli_shortcuts() {
|
fn handle_cli_shortcuts() {
|
||||||
|
use std::io::Write;
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() < 3 {
|
if args.len() < 3 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cmd = args[1].as_str();
|
let cmd = args[1].as_str();
|
||||||
|
if cmd != "--pin" && cmd != "--unpin" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let path = std::path::PathBuf::from(&args[2]);
|
let path = std::path::PathBuf::from(&args[2]);
|
||||||
|
|
||||||
|
let log_path = dirs::data_local_dir()
|
||||||
|
.unwrap_or_else(|| std::path::PathBuf::from("."))
|
||||||
|
.join("MiniCloud Sync")
|
||||||
|
.join("cli.log");
|
||||||
|
if let Some(p) = log_path.parent() {
|
||||||
|
let _ = std::fs::create_dir_all(p);
|
||||||
|
}
|
||||||
|
let log = |msg: &str| {
|
||||||
|
if let Ok(mut f) = std::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&log_path)
|
||||||
|
{
|
||||||
|
let _ = writeln!(f, "[{}] {}", chrono::Utc::now().to_rfc3339(), msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log(&format!("CLI invoked: {} {}", cmd, path.display()));
|
||||||
let result = match cmd {
|
let result = match cmd {
|
||||||
"--pin" => cloud_files::pin_file(&path),
|
"--pin" => cloud_files::pin_file(&path),
|
||||||
"--unpin" => cloud_files::unpin_file(&path),
|
"--unpin" => cloud_files::unpin_file(&path),
|
||||||
_ => return,
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if let Err(e) = result {
|
match &result {
|
||||||
eprintln!("[cloud_files CLI] {cmd} failed: {e}");
|
Ok(()) => log(&format!("{cmd} OK: {}", path.display())),
|
||||||
std::process::exit(1);
|
Err(e) => log(&format!("{cmd} FAILED: {e}")),
|
||||||
}
|
}
|
||||||
std::process::exit(0);
|
std::process::exit(if result.is_ok() { 0 } else { 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue