patch: reduce logic in Fuse and SystemClipboard

1. also added more observability

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
ClSlaid 2023-10-17 16:57:55 +08:00
parent 8f9ba44c2c
commit fbb1d9247f
No known key found for this signature in database
GPG Key ID: E0A5F564C51C056E
6 changed files with 133 additions and 322 deletions

View File

@ -181,7 +181,7 @@ impl FuseServer {
}
impl FuseServer {
pub fn serve(&mut self, reply: ClipboardFile) -> Result<(), CliprdrError> {
pub fn serve(&self, reply: ClipboardFile) -> Result<(), CliprdrError> {
self.tx.send(reply).map_err(|e| {
log::error!("failed to serve cliprdr reply from endpoint: {:?}", e);
CliprdrError::ClipboardInternalError
@ -190,6 +190,15 @@ impl FuseServer {
}
}
impl FuseServer {
pub fn load_file_list(&mut self, files: Vec<FileDescription>) -> Result<(), CliprdrError> {
let tree = FuseNode::build_tree(files)?;
self.files = tree;
self.generation.fetch_add(1, Ordering::Relaxed);
Ok(())
}
}
impl fuser::Filesystem for FuseServer {
fn init(
&mut self,
@ -499,165 +508,11 @@ impl FuseServer {
paths
}
/// fetch file list from remote
fn sync_file_system(
&mut self,
conn_id: i32,
file_group_format_id: i32,
_file_contents_format_id: i32,
) -> Result<bool, CliprdrError> {
let resp = self.send_sync_fs_request(conn_id, file_group_format_id, self.timeout)?;
let descs = match resp {
ClipboardFile::FormatDataResponse {
msg_flags,
format_data,
} => {
if msg_flags != 0x1 {
log::error!("clipboard FUSE server: received unexpected response flags");
return Err(CliprdrError::ClipboardInternalError);
}
let descs = FileDescription::parse_file_descriptors(format_data, conn_id)?;
descs
}
_ => {
log::error!("clipboard FUSE server: received unexpected response type");
return Err(CliprdrError::ClipboardInternalError);
}
};
let mut new_tree = FuseNode::build_tree(descs)?;
let res = new_tree
.iter_mut()
.filter(|f_node| f_node.is_file() && f_node.attributes.size == 0)
.try_for_each(|f_node| self.sync_node_size(f_node));
if let Err(err) = res {
log::error!(
"clipboard FUSE server: failed to fetch file size: {:?}",
err
);
return Err(CliprdrError::ClipboardInternalError);
}
// replace current file system
self.files = new_tree;
self.generation.fetch_add(1, Ordering::Relaxed);
Ok(true)
}
fn send_sync_fs_request(
&self,
conn_id: i32,
file_group_format_id: i32,
timeout: std::time::Duration,
) -> Result<ClipboardFile, CliprdrError> {
// request file list
let data = ClipboardFile::FormatDataRequest {
requested_format_id: file_group_format_id,
};
send_data(conn_id, data);
self.rx.recv_timeout(timeout).map_err(|e| {
log::error!("failed to receive file list from channel: {:?}", e);
CliprdrError::ClipboardInternalError
})
}
pub fn update_files(
&mut self,
conn_id: i32,
file_group_format_id: i32,
file_contents_format_id: i32,
) -> Result<bool, CliprdrError> {
self.sync_file_system(conn_id, file_group_format_id, file_contents_format_id)
}
/// allocate a new file descriptor
fn alloc_fd(&self) -> u64 {
self.file_handle_counter.fetch_add(1, Ordering::Relaxed)
}
// synchronize metadata with remote
fn sync_node_size(&self, node: &mut FuseNode) -> Result<(), std::io::Error> {
log::debug!(
"syncing metadata for {:?} on stream: {}",
node.name,
node.stream_id
);
let request = ClipboardFile::FileContentsRequest {
stream_id: node.stream_id,
list_index: node.inode as i32 - 2, // list index at least 2
dw_flags: 1,
n_position_low: 0,
n_position_high: 0,
cb_requested: 8,
have_clip_data_id: false,
clip_data_id: 0,
};
send_data(node.conn_id, request);
log::debug!(
"waiting for metadata sync reply for {:?} on channel {}",
node.name,
node.conn_id
);
let reply = self
.rx
.recv_timeout(self.timeout)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::TimedOut, e))?;
log::debug!(
"got metadata sync reply for {:?} on channel {}",
node.name,
node.conn_id
);
let size = match reply {
ClipboardFile::FileContentsResponse {
msg_flags,
stream_id,
requested_data,
} => {
if stream_id != node.stream_id {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"stream id mismatch",
));
}
if msg_flags & 1 == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failure request",
));
}
if requested_data.len() != 8 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"invalid data length",
));
}
let little_endian_value = u64::from_le_bytes(requested_data.try_into().unwrap());
little_endian_value
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"invalid reply",
));
}
};
log::debug!("got metadata sync reply for {:?}: size {}", node.name, size);
node.attributes.size = size;
Ok(())
}
fn read_node(
&self,
node: &FuseNode,
@ -916,6 +771,7 @@ impl FuseNode {
}
}
#[allow(unused)]
pub fn is_file(&self) -> bool {
self.attributes.kind == FileType::File
}

View File

@ -14,10 +14,12 @@ use hbb_common::{
log,
};
use lazy_static::lazy_static;
use parking_lot::{Mutex, RwLock};
use parking_lot::Mutex;
use utf16string::WString;
use crate::{send_data, ClipboardFile, CliprdrError, CliprdrServiceContext};
use crate::{
platform::fuse::FileDescription, send_data, ClipboardFile, CliprdrError, CliprdrServiceContext,
};
use super::{fuse::FuseServer, LDAP_EPOCH_DELTA};
@ -44,13 +46,11 @@ fn add_remote_format(local_name: &str, remote_id: i32) {
}
trait SysClipboard: Send + Sync {
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
fn stop(&self);
fn start(&self);
/// send to 0 will send to all channels
fn send_format_list(&self, conn_id: i32) -> Result<(), CliprdrError>;
/// send to 0 will send to all channels
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError>;
fn stop(&self);
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
fn get_file_list(&self) -> Result<Vec<LocalFile>, CliprdrError>;
}
fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> {
@ -312,7 +312,6 @@ pub struct ClipboardContext {
fuse_server: Arc<Mutex<FuseServer>>,
file_list: RwLock<Vec<LocalFile>>,
clipboard: Arc<dyn SysClipboard>,
}
@ -328,13 +327,11 @@ impl ClipboardContext {
let clipboard = get_sys_clipboard(&fuse_mount_point)?;
let clipboard = Arc::from(clipboard) as Arc<_>;
let file_list = RwLock::new(vec![]);
Ok(Self {
fuse_mount_point,
fuse_server,
fuse_handle: Mutex::new(None),
file_list,
clipboard,
})
}
@ -379,10 +376,6 @@ impl ClipboardContext {
Ok(())
}
pub fn stop(&self) -> Result<(), CliprdrError> {
self.set_is_stopped()
}
/// set clipboard data from file list
pub fn set_clipboard(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
let prefix = self.fuse_mount_point.clone();
@ -404,7 +397,7 @@ impl ClipboardContext {
stream_id,
file_idx,
} => {
let file_list = self.file_list.read();
let file_list = self.clipboard.get_file_list()?;
let Some(file) = file_list.get(file_idx) else {
log::error!(
"invalid file index {} requested from conn: {}",
@ -436,7 +429,7 @@ impl ClipboardContext {
offset,
length,
} => {
let file_list = self.file_list.read();
let file_list = self.clipboard.get_file_list()?;
let Some(file) = file_list.get(file_idx) else {
log::error!(
"invalid file index {} requested from conn: {}",
@ -523,33 +516,6 @@ impl ClipboardContext {
self.fuse_handle.lock().is_none()
}
pub fn set_is_stopped(&self) -> Result<(), CliprdrError> {
if self.is_stopped() {
log::debug!("cliprdr already stopped");
return Ok(());
}
// unmount the fuse
if let Some(fuse_handle) = self.fuse_handle.lock().take() {
fuse_handle.join();
}
self.clipboard.stop();
Ok(())
}
pub fn empty_clipboard(&self, conn_id: i32) -> Result<bool, CliprdrError> {
// gc all files, the clipboard is going to shutdown
if self.is_stopped() {
log::debug!("cliprdr stopped, skip emptying clipboard");
return Ok(true);
}
self.fuse_server.lock().update_files(
conn_id,
FILEDESCRIPTOR_FORMAT_ID,
FILECONTENTS_FORMAT_ID,
)
}
pub fn serve(&self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> {
if self.is_stopped() {
log::debug!("cliprdr stopped, restart it");
@ -562,9 +528,8 @@ impl ClipboardContext {
ClipboardFile::MonitorReady => {
log::debug!("server_monitor_ready called");
// ignore capabilities for now
self.send_file_list(conn_id)?;
self.clipboard.send_file_list(0)?;
Ok(())
}
@ -595,17 +560,19 @@ impl ClipboardContext {
add_remote_format(FILECONTENTS_FORMAT_NAME, file_contents_id);
add_remote_format(FILEDESCRIPTORW_FORMAT_NAME, file_descriptor_id);
self.fuse_server.lock().update_files(
conn_id,
file_descriptor_id,
file_contents_id,
)?;
// sync file system from peer
let data = ClipboardFile::FormatDataRequest {
requested_format_id: file_descriptor_id,
};
send_data(conn_id, data);
Ok(())
}
ClipboardFile::FormatListResponse { msg_flags } => {
log::debug!("server_format_list_response called");
if msg_flags != 0x1 {
self.clipboard.send_format_list(conn_id)
send_format_list(conn_id)
} else {
Ok(())
}
@ -625,7 +592,7 @@ impl ClipboardContext {
};
if format == FILEDESCRIPTORW_FORMAT_NAME {
self.clipboard.send_file_list(conn_id)?;
self.send_file_list(conn_id)?;
} else if format == FILECONTENTS_FORMAT_NAME {
log::error!(
"try to read file contents with FormatDataRequest from conn={}",
@ -642,13 +609,27 @@ impl ClipboardContext {
}
Ok(())
}
ClipboardFile::FormatDataResponse { .. } => {
// we don't know its corresponding request, no resend can be performed
ClipboardFile::FormatDataResponse {
msg_flags,
format_data,
} => {
log::debug!("server_format_data_response called");
let mut fuse_server = self.fuse_server.lock();
fuse_server.serve(msg)?;
let paths = fuse_server.list_root();
if msg_flags != 0x1 {
resp_format_data_failure(conn_id);
return Ok(());
}
// this must be a file descriptor format data
let files = FileDescription::parse_file_descriptors(format_data.into(), conn_id)?;
let paths = {
let mut fuse_guard = self.fuse_server.lock();
fuse_guard.load_file_list(files)?;
fuse_guard.list_root()
};
self.set_clipboard(&paths)?;
Ok(())
}
@ -693,16 +674,30 @@ impl ClipboardContext {
}
}
}
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
let file_list = self.clipboard.get_file_list()?;
let paths = file_list.into_iter().map(|lf| lf.path).collect();
send_file_list(paths, conn_id)
}
}
impl CliprdrServiceContext for ClipboardContext {
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
self.stop()
// unmount the fuse
if let Some(fuse_handle) = self.fuse_handle.lock().take() {
fuse_handle.join();
}
self.clipboard.stop();
Ok(())
}
fn empty_clipboard(&mut self, _conn_id: i32) -> Result<bool, CliprdrError> {
self.clipboard.set_file_list(&[])?;
Ok(true)
}
fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> {
self.serve(conn_id, msg)
}
@ -715,3 +710,43 @@ fn resp_format_data_failure(conn_id: i32) {
};
send_data(conn_id, data)
}
fn send_format_list(conn_id: i32) -> Result<(), CliprdrError> {
log::debug!("send format list to remote, conn={}", conn_id);
let fd_format_name = get_local_format(FILEDESCRIPTOR_FORMAT_ID)
.unwrap_or(FILEDESCRIPTORW_FORMAT_NAME.to_string());
let fc_format_name =
get_local_format(FILECONTENTS_FORMAT_ID).unwrap_or(FILECONTENTS_FORMAT_NAME.to_string());
let format_list = ClipboardFile::FormatList {
format_list: vec![
(FILEDESCRIPTOR_FORMAT_ID, fd_format_name),
(FILECONTENTS_FORMAT_ID, fc_format_name),
],
};
send_data(conn_id, format_list);
log::debug!("format list to remote dispatched, conn={}", conn_id);
Ok(())
}
fn send_file_list(paths: Vec<PathBuf>, conn_id: i32) -> Result<(), CliprdrError> {
log::debug!("send file list to remote, conn={}", conn_id);
let files = construct_file_list(paths.as_slice())?;
let mut data = BytesMut::with_capacity(4 + 592 * files.len());
data.put_u32_le(paths.len() as u32);
for file in files.iter() {
data.put(file.as_bin().as_slice());
}
let format_data = data.to_vec();
send_data(
conn_id,
ClipboardFile::FormatDataResponse {
msg_flags: 1,
format_data,
},
);
Ok(())
}

View File

@ -4,21 +4,15 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};
use hbb_common::{
bytes::{BufMut, BytesMut},
log,
};
use hbb_common::log;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use x11_clipboard::Clipboard;
use x11rb::protocol::xproto::Atom;
use crate::{
platform::linux::{
construct_file_list, FILECONTENTS_FORMAT_ID, FILECONTENTS_FORMAT_NAME,
FILEDESCRIPTORW_FORMAT_NAME, FILEDESCRIPTOR_FORMAT_ID,
},
send_data, ClipboardFile, CliprdrError,
platform::linux::{construct_file_list, send_format_list},
CliprdrError,
};
use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard};
@ -175,89 +169,8 @@ impl SysClipboard for X11Clipboard {
log::debug!("stop listening file related atoms on clipboard");
}
fn send_format_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
if self.is_stopped() {
log::debug!("clipboard stopped, skip sending");
return Ok(());
}
let Some(paths) = self.wait_file_list()? else {
log::debug!("no files in format list, skip sending");
return Ok(());
};
let filtered: Vec<_> = paths
.into_iter()
.filter(|pb| !pb.starts_with(&self.ignore_path))
.collect();
if filtered.is_empty() {
log::debug!("no files in format list, skip sending");
return Ok(());
}
send_format_list(conn_id)
}
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
if self.is_stopped() {
log::debug!("clipboard stopped, skip sending");
return Ok(());
}
let Some(paths) = self.wait_file_list()? else {
log::debug!("no files in format list, skip sending");
return Ok(());
};
let filtered: Vec<_> = paths
.into_iter()
.filter(|pb| !pb.starts_with(&self.ignore_path))
.collect();
if filtered.is_empty() {
log::debug!("no files in format list, skip sending");
return Ok(());
}
send_file_list(filtered, conn_id)
fn get_file_list(&self) -> Result<Vec<super::LocalFile>, CliprdrError> {
let paths = { self.former_file_list.lock().clone() };
construct_file_list(&paths)
}
}
fn send_format_list(conn_id: i32) -> Result<(), CliprdrError> {
log::debug!("send format list to remote, conn={}", conn_id);
let format_list = ClipboardFile::FormatList {
format_list: vec![
(
FILEDESCRIPTOR_FORMAT_ID,
FILEDESCRIPTORW_FORMAT_NAME.to_string(),
),
(FILECONTENTS_FORMAT_ID, FILECONTENTS_FORMAT_NAME.to_string()),
],
};
send_data(conn_id, format_list);
log::debug!("format list to remote dispatched, conn={}", conn_id);
Ok(())
}
fn send_file_list(paths: Vec<PathBuf>, conn_id: i32) -> Result<(), CliprdrError> {
log::debug!("send file list to remote, conn={}", conn_id);
let files = construct_file_list(paths.as_slice())?;
let mut data = BytesMut::with_capacity(4 + 592 * files.len());
data.put_u32_le(paths.len() as u32);
for file in files.iter() {
data.put(file.as_bin().as_slice());
}
let format_data = data.to_vec();
send_data(
conn_id,
ClipboardFile::FormatDataResponse {
msg_flags: 1,
format_data,
},
);
Ok(())
}

View File

@ -317,6 +317,7 @@ impl<T: InvokeUiSession> Remote<T> {
if stop {
ContextSend::set_is_stopped();
} else {
log::debug!("Send system clipboard message to remote");
let msg = crate::clipboard_file::clip_2_msg(clip);
allow_err!(peer.send(&msg).await);
}
@ -1714,6 +1715,7 @@ impl<T: InvokeUiSession> Remote<T> {
#[cfg(any(target_os = "windows", target_os = "linux"))]
fn handle_cliprdr_msg(&self, clip: hbb_common::message_proto::Cliprdr) {
log::debug!("handling cliprdr msg from server peer");
#[cfg(feature = "flutter")]
if let Some(hbb_common::message_proto::cliprdr::Union::FormatList(_)) = &clip.union {
if self.client_conn_id
@ -1723,20 +1725,23 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) {
let is_stopping_allowed = clip.is_stopping_allowed_from_peer();
let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v;
let stop = is_stopping_allowed && !file_transfer_enabled;
log::debug!(
let Some(clip) = crate::clipboard_file::msg_2_clip(clip) else {
log::warn!("failed to decode cliprdr msg from server peer");
return;
};
let is_stopping_allowed = clip.is_stopping_allowed_from_peer();
let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v;
let stop = is_stopping_allowed && !file_transfer_enabled;
log::debug!(
"Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}",
stop, is_stopping_allowed, file_transfer_enabled);
if !stop {
let _ = ContextSend::proc(|context| -> ResultType<()> {
context
.server_clip_file(self.client_conn_id, clip)
.map_err(|e| e.into())
});
}
if !stop {
let _ = ContextSend::proc(|context| -> ResultType<()> {
context
.server_clip_file(self.client_conn_id, clip)
.map_err(|e| e.into())
});
}
}
}

View File

@ -480,6 +480,7 @@ impl Connection {
}
#[cfg(any(target_os="windows", target_os="linux"))]
ipc::Data::ClipboardFile(clip) => {
log::debug!("got clipfile from rx_from_cm, send to stream: {:?}", clip);
allow_err!(conn.stream.send(&clip_2_msg(clip)).await);
}
ipc::Data::PrivacyModeState((_, state)) => {
@ -1785,10 +1786,11 @@ impl Connection {
update_clipboard(_cb, None);
}
}
Some(message::Union::Cliprdr(_clip)) =>
{
Some(message::Union::Cliprdr(_clip)) => {
log::debug!("got cliprdr file from connection:{:?}", _clip);
#[cfg(any(target_os = "windows", target_os = "linux"))]
if let Some(clip) = msg_2_clip(_clip) {
log::debug!("send cliprdr file from connection to cm");
self.send_to_cm(ipc::Data::ClipboardFile(clip))
}
}

View File

@ -425,7 +425,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Data::ClipboardFile(_clip) => {
#[cfg(any(windows, linux))]
#[cfg(any(target_os = "windows", target_os="linux"))]
{
let is_stopping_allowed = _clip.is_stopping_allowed_from_peer();
let is_clipboard_enabled = ContextSend::is_enabled();