From 63e719fadf45755b623eb96c8796eaf195f51b37 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 17 Oct 2025 16:33:08 -0600 Subject: [PATCH] Disallow rename/copy/delete on unshared files (#40540) Release Notes: - Disallow rename/delete/copy on unshared files Co-Authored-By: Cole --- crates/project/src/worktree_store.rs | 33 ++++++++++++++++++++++- crates/project_panel/src/project_panel.rs | 8 +++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/project/src/worktree_store.rs b/crates/project/src/worktree_store.rs index 5da8fe2d0070e77c2294465bde4423a13f8ec9ec..670b405ed33757117ec62bfbbb4c947f79e5026a 100644 --- a/crates/project/src/worktree_store.rs +++ b/crates/project/src/worktree_store.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, atomic::AtomicUsize}, }; -use anyhow::{Context as _, Result, anyhow}; +use anyhow::{Context as _, Result, anyhow, bail}; use collections::{HashMap, HashSet}; use fs::{Fs, copy_recursive}; use futures::{ @@ -1203,6 +1203,16 @@ impl WorktreeStore { RelPath::from_proto(&envelope.payload.new_path)?, ); let (scan_id, entry) = this.update(&mut cx, |this, cx| { + let Some((_, project_id)) = this.downstream_client else { + bail!("no downstream client") + }; + let Some(entry) = this.entry_for_id(entry_id, cx) else { + bail!("no such entry"); + }; + if entry.is_private && project_id != REMOTE_SERVER_PROJECT_ID { + bail!("entry is private") + } + let new_worktree = this .worktree_for_id(new_worktree_id, cx) .context("no such worktree")?; @@ -1226,6 +1236,15 @@ impl WorktreeStore { ) -> Result { let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id); let worktree = this.update(&mut cx, |this, cx| { + let Some((_, project_id)) = this.downstream_client else { + bail!("no downstream client") + }; + let Some(entry) = this.entry_for_id(entry_id, cx) else { + bail!("no entry") + }; + if entry.is_private && project_id != REMOTE_SERVER_PROJECT_ID { + bail!("entry is private") + } this.worktree_for_entry(entry_id, cx) .context("worktree not found") })??; @@ -1246,6 +1265,18 @@ impl WorktreeStore { let worktree = this .worktree_for_entry(entry_id, cx) .context("no such worktree")?; + + let Some((_, project_id)) = this.downstream_client else { + bail!("no downstream client") + }; + let entry = worktree + .read(cx) + .entry_for_id(entry_id) + .ok_or_else(|| anyhow!("missing entry"))?; + if entry.is_private && project_id != REMOTE_SERVER_PROJECT_ID { + bail!("entry is private") + } + let scan_id = worktree.read(cx).scan_id(); anyhow::Ok(( scan_id, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 17c09dcb06e6f665eb589fc4b61b5bc73a0c2982..0160d4d151d5bd102e72e195127fd6a16b63ce15 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -64,7 +64,7 @@ use workspace::{ DraggedSelection, OpenInTerminal, OpenOptions, OpenVisible, PreviewTabsSettings, SelectedEntry, SplitDirection, Workspace, dock::{DockPosition, Panel, PanelEvent}, - notifications::{DetachAndPromptErr, NotifyTaskExt}, + notifications::{DetachAndPromptErr, NotifyResultExt, NotifyTaskExt}, }; use worktree::CreatedEntry; use zed_actions::workspace::OpenWithSystem; @@ -2677,12 +2677,14 @@ impl ProjectPanel { for task in paste_tasks { match task { PasteTask::Rename(task) => { - if let Some(CreatedEntry::Included(entry)) = task.await.log_err() { + if let Some(CreatedEntry::Included(entry)) = + task.await.notify_async_err(cx) + { last_succeed = Some(entry); } } PasteTask::Copy(task) => { - if let Some(Some(entry)) = task.await.log_err() { + if let Some(Some(entry)) = task.await.notify_async_err(cx) { last_succeed = Some(entry); } }