fix(prompt) cached file open instead of actualy files

This commit is contained in:
nvrl committed 2026-08-11 14:22:13 +02:00
1 parent 4d76462e62
commit 14b6df7bce
18 files changed
+1863 -60

No files matched your search

+10 -5
View File
@@ -332,15 +332,20 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
}
}
}
Ok(CtlRequest::OpenFile { path }) => match rel_to_mount(mountpoint, &path) {
Ok(rel) => match core.open_file(&rel) {
// A uid addresses the node wherever it lives; the path only works for
// the primary mount's tree. Prefer the uid when the client sent one.
Ok(CtlRequest::OpenFile { path, uid }) => {
let opened = match uid.as_deref() {
Some(uid) => core.open_file_uid(uid),
None => rel_to_mount(mountpoint, &path).and_then(|rel| core.open_file(&rel)),
};
match opened {
Ok(p) => CtlResponse::FilePath {
path: p.display().to_string(),
},
Err(e) => CtlResponse::error(e),
},
Err(e) => CtlResponse::error(e),
},
}
}
Ok(CtlRequest::Search { query, limit }) => match core.search(&query, limit) {
Ok(hits) => CtlResponse::SearchResults { hits },
Err(e) => CtlResponse::error(e),
+77 -7
View File
@@ -2974,15 +2974,24 @@ impl Core {
.collect())
}
/// Return an absolute path through the most specific live on-demand mount
/// Return an absolute path through the most specific local location
/// covering `uid`. A node may be below several configured roots, so prefer
/// the root leaving the shortest descendant path.
///
/// An on-demand folder only answers while its mount is live — without the
/// mount its `local_path` is an ordinary empty directory. A mirror folder
/// needs no mount: its files are real local copies the sync loop pushes
/// back, so it is a valid open target whenever it is configured.
fn mounted_search_path(&self, uid: &str) -> Option<String> {
let live_mounts = self.mounts.lock();
let folders = self.db.sync_folder_list().ok()?;
folders
.into_iter()
.filter(|folder| folder.mode == "ondemand" && live_mounts.contains_key(&folder.id))
.filter(|folder| match folder.mode.as_str() {
"ondemand" => live_mounts.contains_key(&folder.id),
"mirror" => true,
_ => false,
})
.filter_map(|folder| {
let relative = self.db.path_relative_to(&folder.remote_uid, uid).ok()??;
let depth = relative.split('/').count();
@@ -3164,7 +3173,68 @@ impl Core {
node_size(&e.node),
)
};
if let Some(p) = self.cache.cached_content_path(&uid, mtime, size) {
self.fetch_content(&uid, &name, mtime, size)
}
/// [`open_file`](Self::open_file) addressed by uid instead of by path.
///
/// A search hit is a metadata-index row, and the index covers every
/// location this daemon knows — including on-demand sync folders and
/// mirrors, whose nodes have no path inside the primary mount's tree.
/// Walking such a hit's path from the mount root answers ENOENT even though
/// the node is perfectly reachable, so front-ends address hits by uid.
///
/// Metadata comes from the live tree, then the index, then the API — the
/// last covers a node the index knows but no location holds. Unlike the
/// mutating by-uid handlers this does not demand a
/// [`resolve_anywhere`](Self::resolve_anywhere) residency proof: reading
/// one's own file is what the API authorizes anyway, and requiring
/// residency is exactly what turned an openable search hit into "no such
/// file or folder". [`open_shared_file`](Self::open_shared_file) already
/// works this way. Reserved (local/virtual) uids name no remote node and
/// are still rejected.
fn open_file_uid(&self, raw_uid: &str) -> CoreResult<PathBuf> {
let uid = parse_uid(raw_uid)
.filter(|uid| !is_local_uid(uid) && !is_virtual_uid(uid))
.ok_or_else(|| CoreError::invalid(format!("invalid uid: {raw_uid}")))?;
let live = self
.state
.lock()
.entries
.values()
.find(|e| e.uid == uid)
.map(|e| e.node.clone());
let node = match live {
Some(node) => node,
None => match self
.db
.node_by_uid(&uid.to_string())
.map_err(|e| CoreError::from_api(&e, "load node"))?
{
Some(node) => node,
None => self
.rt
.block_on(self.client.get_node(&uid))
.map_err(|e| CoreError::from_api(&e, "get node"))?
.ok_or_else(|| CoreError::not_found("node vanished"))?,
},
};
if !node.is_file() {
return Err(CoreError::invalid("not a regular file"));
}
self.fetch_content(&uid, &node.name, node.modification_time, node_size(&node))
}
/// Materialise a file's full content into the content cache, returning its
/// on-disk path. Serves a fresh cached blob without touching the network.
fn fetch_content(
&self,
uid: &NodeUid,
name: &str,
mtime: i64,
size: u64,
) -> CoreResult<PathBuf> {
if let Some(p) = self.cache.cached_content_path(uid, mtime, size) {
return Ok(p);
}
@@ -3175,7 +3245,7 @@ impl Core {
// from sharing a partially written staging file.
static OPEN_TEMP_SEQ: AtomicU64 = AtomicU64::new(0);
let seq = OPEN_TEMP_SEQ.fetch_add(1, Ordering::Relaxed);
let tmp = self.cache.content_path(&uid).with_extension(format!(
let tmp = self.cache.content_path(uid).with_extension(format!(
"open-{}-{}-{seq}.tmp",
std::process::id(),
SystemTime::now()
@@ -3188,7 +3258,7 @@ impl Core {
.create_new(true)
.open(&tmp)
.map_err(|e| CoreError::internal(format!("create download temp: {e}")))?;
let file = match self.download_file_tracked_to(&uid, &name, size, file) {
let file = match self.download_file_tracked_to(uid, name, size, file) {
Ok(file) => file,
Err(e) => {
let _ = std::fs::remove_file(&tmp);
@@ -3199,12 +3269,12 @@ impl Core {
let _ = std::fs::remove_file(&tmp);
return Err(CoreError::internal(format!("sync download temp: {e}")));
}
if let Err(e) = self.cache.store_file(&uid, mtime, size, &tmp) {
if let Err(e) = self.cache.store_file(uid, mtime, size, &tmp) {
let _ = std::fs::remove_file(&tmp);
return Err(CoreError::internal(format!("cache store: {e}")));
}
let _ = std::fs::remove_file(&tmp);
Ok(self.cache.content_path(&uid))
Ok(self.cache.content_path(uid))
}
/// Drop the cached child listing of `rel`'s parent directory so the next