Skip to content

Commit 873f110

Browse files
committed
Bugfix: MTP notify_mutation matches cached listings again
`MtpVolume::write_from_stream`'s belt-and-suspenders cache patch was silently no-op because the parent path we threaded into `notify_directory_changed` was the volume-relative form callers handed us (`/file-a.txt` after the cross-volume orchestrator did `dest_path.join(name)` with `dest_path = "/"`), while every cached listing on `LISTING_CACHE` stores the absolute MTP URL form (`mtp://{device}/{storage}/...`). `find_listings_for_path_on_volume` matches by exact path equality, so the lookup missed every time and the destination pane stayed stale until the lossy MTP USB event loop caught up — which on many devices it never does. Add `MtpVolume::to_url_path` as the inverse of `to_mtp_path` and run every caller-supplied path through it at the top of `notify_mutation`, including the `get_metadata` lookup for the new/renamed entry. Every existing call site (`create_directory`, `delete`, `rename` cross-/same-dir, `write_from_stream`) benefits because they all funnel through the same trait method. Confirmed via tempdebug logging on a local-to-MTP copy: pre-fix `listings_matched=0` (and only the FS-watcher fallback could rescue the pane); post-fix the lookup finds the right listing and the file appears in the destination pane immediately.
1 parent e852f04 commit 873f110

1 file changed

Lines changed: 34 additions & 5 deletions

File tree

  • apps/desktop/src-tauri/src/file_system/volume

apps/desktop/src-tauri/src/file_system/volume/mtp.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,30 @@ impl MtpVolume {
9191
// Strip leading slash if present
9292
path_str.strip_prefix('/').unwrap_or(&path_str).to_string()
9393
}
94+
95+
/// Normalizes any caller-supplied path on this volume to the canonical
96+
/// absolute MTP URL (`mtp://{device_id}/{storage_id}[/inner/path]`).
97+
///
98+
/// `notify_directory_changed` matches `LISTING_CACHE` entries by exact path
99+
/// equality. Cached listings always store the absolute URL form because
100+
/// pane navigation feeds the URL into the listing pipeline. Write/delete
101+
/// callers, however, may hand us a volume-relative path (e.g. `/file-a.txt`
102+
/// after the cross-volume copy orchestrator does `dest_path.join(name)`
103+
/// with `dest_path = "/"`). Without this conversion the lookup misses
104+
/// every time and the cache patch is silently dropped, leaving the
105+
/// destination pane stale until the lossy MTP event loop catches up.
106+
fn to_url_path(&self, path: &Path) -> PathBuf {
107+
let path_str = path.to_string_lossy();
108+
if path_str.starts_with("mtp://") {
109+
return path.to_path_buf();
110+
}
111+
let inner = self.to_mtp_path(path);
112+
if inner.is_empty() {
113+
self.root.clone()
114+
} else {
115+
self.root.join(inner)
116+
}
117+
}
94118
}
95119

96120
impl Volume for MtpVolume {
@@ -256,20 +280,25 @@ impl Volume for MtpVolume {
256280
Box::pin(async move {
257281
use crate::file_system::listing::caching::{DirectoryChange, notify_directory_changed};
258282

283+
// Normalize once so every branch (incl. get_metadata's parent
284+
// lookup) sees the canonical absolute MTP URL. Callers happily
285+
// hand us either form depending on which layer they came from.
286+
let parent_url = self.to_url_path(parent_path);
287+
let parent_ref = parent_url.as_path();
259288
// MTP's get_metadata lists the parent dir to find the entry, which is expensive
260289
// but correct. The MTP event loop (connection/event_loop.rs) also handles
261290
// change notifications, so this is belt-and-suspenders for self-mutations.
262291
match mutation {
263292
MutationEvent::Created(ref name) | MutationEvent::Modified(ref name) => {
264-
let entry_path = parent_path.join(name);
293+
let entry_path = parent_ref.join(name);
265294
match self.get_metadata(&entry_path).await {
266295
Ok(entry) => {
267296
let change = if matches!(mutation, MutationEvent::Created(_)) {
268297
DirectoryChange::Added(entry)
269298
} else {
270299
DirectoryChange::Modified(entry)
271300
};
272-
notify_directory_changed(&self.volume_id, parent_path, change);
301+
notify_directory_changed(&self.volume_id, parent_ref, change);
273302
}
274303
Err(e) => {
275304
debug!(
@@ -281,15 +310,15 @@ impl Volume for MtpVolume {
281310
}
282311
}
283312
MutationEvent::Deleted(name) => {
284-
notify_directory_changed(&self.volume_id, parent_path, DirectoryChange::Removed(name));
313+
notify_directory_changed(&self.volume_id, parent_ref, DirectoryChange::Removed(name));
285314
}
286315
MutationEvent::Renamed { from, to } => {
287-
let new_path = parent_path.join(&to);
316+
let new_path = parent_ref.join(&to);
288317
match self.get_metadata(&new_path).await {
289318
Ok(entry) => {
290319
notify_directory_changed(
291320
&self.volume_id,
292-
parent_path,
321+
parent_ref,
293322
DirectoryChange::Renamed {
294323
old_name: from,
295324
new_entry: entry,

0 commit comments

Comments
 (0)