核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
1028 lines
30 KiB
Markdown
1028 lines
30 KiB
Markdown
# russh v0.61.2 API限制分析报告
|
||
|
||
**分析日期:** 2026-06-09
|
||
**分析目标:** 评估russh库在rsync集成中的技术可行性
|
||
**分析范围:** Channel API、数据流处理、Multiplexing、rsync握手流程
|
||
|
||
---
|
||
|
||
## 1. Channel API能力分析
|
||
|
||
### 1.1 channel.data()方法存在性 ✅
|
||
|
||
**结论:** `channel.data()`方法**存在**,但存在关键限制。
|
||
|
||
**API定义(channels/mod.rs:321):**
|
||
```rust
|
||
/// Send data to a channel.
|
||
pub async fn data<R: tokio::io::AsyncRead + Unpin>(&self, data: R) -> Result<(), Error> {
|
||
self.send_data(None, data).await
|
||
}
|
||
|
||
/// Send owned bytes to a channel without copying them into the `AsyncWrite` path.
|
||
pub async fn data_bytes(&self, data: impl Into<Bytes>) -> Result<(), Error> {
|
||
self.send_bytes(None, data.into()).await
|
||
}
|
||
```
|
||
|
||
**关键发现:**
|
||
- ✅ `data()`方法存在,接受`AsyncRead`类型参数
|
||
- ✅ `data_bytes()`方法存在,接受`Bytes`类型参数(零拷贝)
|
||
- ✅ 支持异步发送(`async fn`)
|
||
- ⚠️ **单向数据发送**,无返回值(`Result<(), Error>`)
|
||
- ⚠️ 仅用于**服务器向客户端发送数据**
|
||
|
||
---
|
||
|
||
### 1.2 channel.read()双向流支持 ⚠️
|
||
|
||
**结论:** `channel.read()`方法**不存在**,但有替代方案。
|
||
|
||
**Channel API完整能力矩阵:**
|
||
|
||
| 方法 | 存在性 | 功能 | 适用场景 |
|
||
|------|--------|------|----------|
|
||
| `channel.data()` | ✅ 存在 | 发送数据 | Sender模式 |
|
||
| `channel.wait()` | ✅ 存在 | 接收ChannelMsg | 接收所有消息类型 |
|
||
| `channel.make_reader()` | ✅ 存在 | 创建AsyncRead流 | 接收Data消息 |
|
||
| `channel.into_stream()` | ✅ 存在 | 创建双向Stream | **唯一双向方案** ⭐ |
|
||
| `channel.read()` | ❌ 不存在 | 直接读取数据 | **API缺失** |
|
||
|
||
**替代方案分析:**
|
||
|
||
**方案1:channel.wait() - 接收所有消息类型**
|
||
```rust
|
||
// channels/mod.rs:655
|
||
pub async fn wait(&mut self) -> Option<ChannelMsg> {
|
||
self.read_half.wait().await
|
||
}
|
||
|
||
// ChannelMsg枚举定义(channels/mod.rs:21-114)
|
||
pub enum ChannelMsg {
|
||
Data { data: Bytes }, // ← 数据消息
|
||
ExtendedData { data: Bytes, ext: u32 }, // ← 扩展数据(stderr)
|
||
Eof, // ← EOF信号
|
||
Close, // ← 关闭信号
|
||
Open { ... },
|
||
ExitStatus { exit_status: u32 },
|
||
// ... 其他控制消息
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 可以接收所有消息类型(Data、Eof、Close、ExitStatus等)
|
||
- ✅ 完整的SSH协议消息处理能力
|
||
|
||
**缺点:**
|
||
- ⚠️ 需要手动处理ChannelMsg枚举,代码复杂度高
|
||
- ⚠️ 需要循环调用`wait()`,无法直接作为流使用
|
||
- ⚠️ **不支持AsyncRead trait**,无法直接用于russh-sftp
|
||
|
||
---
|
||
|
||
**方案2:channel.make_reader() - AsyncRead流**
|
||
```rust
|
||
// channels/mod.rs:677
|
||
pub fn make_reader(&mut self) -> impl AsyncRead + '_ {
|
||
self.read_half.make_reader()
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 实现`AsyncRead` trait,可直接用于SFTP等子系统
|
||
- ✅ 自动过滤Data消息,忽略其他控制消息
|
||
- ✅ 支持异步读取
|
||
|
||
**缺点:**
|
||
- ⚠️ **仅支持读取**,无法同时发送数据
|
||
- ⚠️ 需要借用`&mut self`,**无法同时持有channel用于发送**
|
||
- ⚠️ **无法实现双向流**(rsync receiver模式需要)
|
||
|
||
---
|
||
|
||
**方案3:channel.into_stream() - 双向Stream ⭐**
|
||
```rust
|
||
// channels/mod.rs:661
|
||
pub fn into_stream(self) -> ChannelStream<S> {
|
||
ChannelStream::new(
|
||
io::ChannelTx::new(...), // ← 发送端(AsyncWrite)
|
||
io::ChannelRx::new(...), // ← 接收端(AsyncRead)
|
||
)
|
||
}
|
||
```
|
||
|
||
**ChannelStream实现(channels/channel_stream.rs):**
|
||
```rust
|
||
impl<S> AsyncRead for ChannelStream<S> {
|
||
fn poll_read(...) -> Poll<io::Result<()>> {
|
||
Pin::new(&mut self.rx).poll_read(cx, buf) // ← 使用ChannelRx读取
|
||
}
|
||
}
|
||
|
||
impl<S> AsyncWrite for ChannelStream<S> {
|
||
fn poll_write(...) -> Poll<Result<usize, io::Error>> {
|
||
Pin::new(&mut self.tx).poll_write(cx, buf) // ← 使用ChannelTx写入
|
||
}
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ **唯一支持双向流的方案** ⭐
|
||
- ✅ 同时实现`AsyncRead` + `AsyncWrite`
|
||
- ✅ 可以直接用于russh-sftp的`run()`函数
|
||
- ✅ 支持异步读写,符合tokio生态
|
||
|
||
**缺点:**
|
||
- ⚠️ **消耗Channel所有权**(`self` → `into_stream(self)`)
|
||
- ⚠️ 无法再调用`channel.data()`等原始方法
|
||
- ⚠️ 需要在创建stream前准备好所有逻辑
|
||
|
||
---
|
||
|
||
### 1.3 异步读取数据支持 ✅
|
||
|
||
**结论:** 完全支持异步读取。
|
||
|
||
**异步支持证据:**
|
||
```rust
|
||
// ChannelReadHalf.wait() - 异步接收消息
|
||
pub async fn wait(&mut self) -> Option<ChannelMsg> {
|
||
self.receiver.recv().await // ← tokio::sync::mpsc异步接收
|
||
}
|
||
|
||
// ChannelRx.poll_read() - 异步读取流
|
||
impl AsyncRead for ChannelRx<R> {
|
||
fn poll_read(...) -> Poll<io::Result<()>> {
|
||
match ready!(self.channel.borrow_mut().receiver.poll_recv(cx)) {
|
||
Some(msg) => (msg, 0),
|
||
None => return Poll::Ready(Ok(())),
|
||
}
|
||
// ... 处理Data消息
|
||
}
|
||
}
|
||
```
|
||
|
||
**异步特性:**
|
||
- ✅ 基于`tokio::sync::mpsc`的异步channel
|
||
- ✅ 支持`Future`/`async/await`语法
|
||
- ✅ 支持`AsyncRead`/`AsyncWrite` trait
|
||
- ✅ 完整集成tokio生态
|
||
|
||
---
|
||
|
||
## 2. 数据流处理能力分析
|
||
|
||
### 2.1 Sender模式数据发送能力 ✅
|
||
|
||
**结论:** Sender模式完全支持,但仅适用于单向发送。
|
||
|
||
**Sender模式实现(server.rs:177):**
|
||
```rust
|
||
// MarkBase现有实现:rsync sender模式
|
||
if rsync_cmd.is_sender_mode() {
|
||
let data = tokio::fs::read(&file_path).await?;
|
||
|
||
// 发送数据到channel
|
||
channel.data(&send_data[..]).await?; // ← 单向发送
|
||
|
||
// 发送退出状态
|
||
channel.exit_status(0).await?;
|
||
}
|
||
```
|
||
|
||
**支持能力:**
|
||
- ✅ 读取本地文件
|
||
- ✅ 压缩数据(可选)
|
||
- ✅ 通过`channel.data()`发送数据
|
||
- ✅ 发送`exit_status`退出状态
|
||
- ✅ **完整Sender模式支持**
|
||
|
||
**技术限制:**
|
||
- ⚠️ 无法接收客户端的响应(如rsync protocol握手)
|
||
- ⚠️ 无法处理复杂的双向交互协议
|
||
|
||
---
|
||
|
||
### 2.2 Receiver模式数据接收能力 ⚠️
|
||
|
||
**结论:** Receiver模式**理论上支持**,但存在技术障碍。
|
||
|
||
**Receiver模式需求(server.rs:190):**
|
||
```rust
|
||
// MarkBase现有代码中的TODO注释:
|
||
else {
|
||
log::info!("Rsync receiver mode: receiving file {}", file_path);
|
||
|
||
// Receiver模式:接收文件数据
|
||
// 简化实现:等待channel数据(需要russh API支持)
|
||
// 完整实现需要双向数据流处理
|
||
|
||
// TODO: 实现channel.read() - russh API限制 ⚠️
|
||
log::warn!("Rsync receiver mode requires bidirectional stream support");
|
||
|
||
channel.exit_status(1).await?; // ← 暂时不支持
|
||
}
|
||
```
|
||
|
||
**技术障碍分析:**
|
||
|
||
| 障碍类型 | 具体问题 | 解决方案 |
|
||
|---------|----------|----------|
|
||
| **API缺失** | `channel.read()`不存在 | 使用`into_stream()` ⭐ |
|
||
| **所有权冲突** | Channel已被消耗 | 无法再调用`channel.data()` |
|
||
| **消息类型处理** | 需过滤Data消息 | ChannelRx自动过滤 |
|
||
| **协议复杂性** | rsync握手需双向交互 | 需完整协议实现 |
|
||
|
||
**可能的实现路径:**
|
||
|
||
**路径1:使用into_stream() ⭐**
|
||
```rust
|
||
// 理论实现(未验证)
|
||
let stream = channel.into_stream(); // ← 消耗channel所有权
|
||
|
||
// 接收客户端数据
|
||
let mut buf = vec![0u8; 1024];
|
||
let n = stream.read(&mut buf).await?; // ← AsyncRead
|
||
|
||
// 写入本地文件
|
||
tokio::fs::write(&file_path, &buf[..n]).await?;
|
||
|
||
// 发送响应
|
||
stream.write_all(b"ACK").await?; // ← AsyncWrite
|
||
```
|
||
|
||
**路径2:使用wait()循环**
|
||
```rust
|
||
// 理论实现(未验证)
|
||
loop {
|
||
match channel.wait().await {
|
||
Some(ChannelMsg::Data { data }) => {
|
||
// 接收数据
|
||
tokio::fs::write(&file_path, &data).await?;
|
||
}
|
||
Some(ChannelMsg::Eof) => {
|
||
// EOF信号
|
||
channel.exit_status(0).await?;
|
||
break;
|
||
}
|
||
None => break, // ← channel关闭
|
||
_ => {} // ← 忽略其他消息
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.3 双向数据流交互可能性 ⚠️
|
||
|
||
**结论:** **理论上可行**,但需要完整协议实现。
|
||
|
||
**双向流交互需求(rsync protocol):**
|
||
|
||
```
|
||
rsync Protocol握手流程:
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Phase 1: Connection Setup │
|
||
│ - Client sends: "rsync\n" │
|
||
│ - Server responds: "@RSYNCD: 31\n" │
|
||
│ - Client sends: "@RSYNCD: 31\n" │
|
||
└─────────────────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Phase 2: Authentication │
|
||
│ - Client sends command line │
|
||
│ - Server validates user/path │
|
||
│ - Server sends: "@RSYNCD: OK\n" │
|
||
└─────────────────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Phase 3: File Transfer │
|
||
│ - Sender: sends file data + checksums │
|
||
│ - Receiver: sends ACK/requests │
|
||
│ - Multiple round-trips │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**russh双向流能力评估:**
|
||
|
||
| 双向流需求 | russh支持 | 实现难度 |
|
||
|-----------|----------|----------|
|
||
| **Phase 1: 握手** | ✅ 支持 | 中等(需解析协议) |
|
||
| **Phase 2: 认证** | ✅ 支持 | 低(已有bcrypt) |
|
||
| **Phase 3: Sender** | ✅ 支持 | 低(单向发送) |
|
||
| **Phase 3: Receiver** ⚠️ | ⚠️ 部分支持 | **高(双向交互)** |
|
||
|
||
**关键发现:**
|
||
- ✅ Phase 1-2可用`into_stream()`实现
|
||
- ✅ Sender模式可用`channel.data()`实现
|
||
- ⚠️ **Receiver模式需要完整协议状态机** ⚠️
|
||
- ⚠️ rsync protocol未标准化,需参考OpenSSH实现
|
||
|
||
---
|
||
|
||
## 3. Multiplexing支持分析
|
||
|
||
### 3.1 russh是否支持multiplexing protocol ❌
|
||
|
||
**结论:** russh **不支持SSH multiplexing protocol**。
|
||
|
||
**证据分析(client/mod.rs:85):**
|
||
```rust
|
||
/// It is in charge of multiplexing and keeping track of various channels
|
||
```
|
||
|
||
**关键误解澄清:**
|
||
- ⚠️ "multiplexing"在russh源码中指的是**多channel管理**
|
||
- ⚠️ **不是SSH Control Multiplexing(OpenSSH的`ControlMaster`)**
|
||
- ❌ russh不支持ControlMaster/ControlPath协议
|
||
|
||
**SSH Multiplexing vs russh Channel Multiplexing:**
|
||
|
||
| 特性 | SSH Control Multiplexing | russh Channel Multiplexing |
|
||
|------|-------------------------|---------------------------|
|
||
| **定义** | 一个SSH连接承载多个SSH会话 | 一个SSH连接承载多个channel |
|
||
| **协议** | OpenSSH专有协议 | SSH RFC 4254标准协议 |
|
||
| **实现** | ControlMaster + ControlPath | 多个ChannelId并发 |
|
||
| **用途** | 连接复用、减少认证开销 | 并行执行多个命令 |
|
||
| **russh支持** | ❌ 不支持 | ✅ 支持 |
|
||
|
||
**russh Channel Multiplexing实现:**
|
||
```rust
|
||
// Session内部管理多个channels
|
||
pub struct Session {
|
||
channels: HashMap<ChannelId, Channel<Msg>>, // ← 多channel并发
|
||
}
|
||
|
||
// 示例:并发执行多个命令
|
||
let channel1 = session.channel_open_session().await?;
|
||
let channel2 = session.channel_open_session().await?; // ← 第二个channel
|
||
|
||
channel1.exec("ls").await?;
|
||
channel2.exec("pwd").await?; // ← 并发执行
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 Message type区分能力 ✅
|
||
|
||
**结论:** 完全支持Message type区分。
|
||
|
||
**ChannelMsg完整枚举(channels/mod.rs:21-114):**
|
||
```rust
|
||
pub enum ChannelMsg {
|
||
// 数据消息
|
||
Data { data: Bytes }, // ← STDOUT数据
|
||
ExtendedData { data: Bytes, ext: u32 }, // ← STDERR数据(ext=1)
|
||
|
||
// 控制消息
|
||
Open { id, max_packet_size, window_size },
|
||
Eof, // ← EOF信号
|
||
Close, // ← 关闭信号
|
||
|
||
// 客户端请求(client only)
|
||
RequestPty { ... },
|
||
RequestShell { want_reply: bool },
|
||
Exec { want_reply: bool, command: Vec<u8> },
|
||
RequestSubsystem { want_reply: bool, name: String },
|
||
Signal { signal: Sig },
|
||
|
||
// 服务器响应(server only)
|
||
ExitStatus { exit_status: u32 }, // ← 退出状态
|
||
ExitSignal { signal_name, core_dumped, ... },
|
||
Success, // ← 成功响应
|
||
Failure, // ← 失败响应
|
||
WindowAdjusted { new_size: u32 },
|
||
XonXoff { client_can_do: bool },
|
||
|
||
// 其他
|
||
SetEnv { ... },
|
||
WindowChange { ... },
|
||
AgentForward { ... },
|
||
RequestX11 { ... },
|
||
OpenFailure(ChannelOpenFailure),
|
||
}
|
||
```
|
||
|
||
**Message type分类:**
|
||
|
||
| 类别 | Message类型 | 处理方式 |
|
||
|------|-----------|----------|
|
||
| **数据消息** | Data, ExtendedData | 通过ChannelRx读取 |
|
||
| **控制消息** | Eof, Close, ExitStatus | 通过`wait()`接收 |
|
||
| **请求消息** | Exec, RequestSubsystem | 通过Handler trait处理 |
|
||
| **响应消息** | Success, Failure | 通过Handler trait返回 |
|
||
|
||
---
|
||
|
||
### 3.3 控制消息和数据消息分离 ✅
|
||
|
||
**结论:** 完全支持分离。
|
||
|
||
**分离机制:**
|
||
|
||
**方案1:ChannelRx自动过滤**
|
||
```rust
|
||
// channels/io/rx.rs:46
|
||
impl AsyncRead for ChannelRx<R> {
|
||
fn poll_read(...) -> Poll<io::Result<()>> {
|
||
match (&msg, self.ext) {
|
||
(ChannelMsg::Data { data }, None) => {
|
||
// ← 只处理Data消息,自动过滤其他消息
|
||
buf.put_slice(&data[idx..idx + readable]);
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
(ChannelMsg::ExtendedData { data, ext }, Some(target)) if *ext == target => {
|
||
// ← 只处理匹配的ExtendedData
|
||
buf.put_slice(&data[idx..idx + readable]);
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
(ChannelMsg::Eof, _) => {
|
||
// ← EOF信号,关闭流
|
||
self.channel.borrow_mut().receiver.close();
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
_ => {
|
||
// ← 忽略其他控制消息(ExitStatus、Close等)
|
||
cx.waker().wake_by_ref();
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**方案2:Channel.wait()手动分离**
|
||
```rust
|
||
loop {
|
||
match channel.wait().await {
|
||
Some(ChannelMsg::Data { data }) => {
|
||
// ← 处理数据消息
|
||
process_data(data);
|
||
}
|
||
Some(ChannelMsg::ExitStatus { exit_status }) => {
|
||
// ← 处理控制消息
|
||
log::info!("Exit status: {}", exit_status);
|
||
break;
|
||
}
|
||
Some(ChannelMsg::Close) => {
|
||
// ← 处理控制消息
|
||
log::info!("Channel closed");
|
||
break;
|
||
}
|
||
_ => {} // ← 忽略其他消息
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. rsync集成可行性分析
|
||
|
||
### 4.1 完整握手流程实现可能性 ⚠️
|
||
|
||
**结论:** **技术上可行**,但实现难度高。
|
||
|
||
**rsync Protocol握手流程(OpenSSH标准):**
|
||
|
||
```
|
||
Phase 1: Connection Setup(1-2 round-trips)
|
||
┌─────────┐ ┌─────────┐
|
||
│ Client │ │ Server │
|
||
└─────────┘ └─────────┘
|
||
│ │
|
||
│──── "rsync\n" ──────────────>│ (1) Client announces
|
||
│ │
|
||
│<─── "@RSYNCD: 31\n" ─────────│ (2) Server version
|
||
│ │
|
||
│──── "@RSYNCD: 31\n" ─────────>│ (3) Client confirms
|
||
│ │
|
||
|
||
Phase 2: Authentication(1 round-trip)
|
||
│ │
|
||
│──── command line ────────────>│ (4) Client sends command
|
||
│ (e.g., "-e.ssh.lso...") │
|
||
│ │
|
||
│<─── "@RSYNCD: OK\n" ─────────│ (5) Server validates
|
||
│ │
|
||
|
||
Phase 3: File Transfer(Multiple round-trips)
|
||
│ │
|
||
│ Sender模式: │
|
||
│──── File data + checksums ───>│ (6) Sender sends data
|
||
│ │
|
||
│ Receiver模式: │
|
||
│<─── File data requests ──────│ (7) Receiver requests
|
||
│──── ACK + more data ─────────>│ (8) Sender responds
|
||
│ │
|
||
│<─── Exit status ─────────────│ (9) Server exits
|
||
│ │
|
||
```
|
||
|
||
**russh实现分析:**
|
||
|
||
| Phase | 实现难度 | 关键技术 | russh能力 |
|
||
|-------|---------|----------|----------|
|
||
| **Phase 1** | 中等 | 协议解析 + 字符串匹配 | ✅ `into_stream()` |
|
||
| **Phase 2** | 低 | 命令解析 + 认证验证 | ✅ 已有bcrypt |
|
||
| **Phase 3: Sender** | 低 | 单向数据发送 | ✅ `channel.data()` |
|
||
| **Phase 3: Receiver** ⚠️ | **高** | **双向交互协议** | ⚠️ 需完整实现 |
|
||
|
||
**技术障碍:**
|
||
|
||
**障碍1:Protocol未标准化**
|
||
- ⚠️ rsync protocol未公开RFC文档
|
||
- ⚠️ 需参考OpenSSH源码实现
|
||
- ⚠️ 版本兼容性问题(rsync 31.x vs 30.x)
|
||
|
||
**障碍2:双向交互复杂**
|
||
- ⚠️ Receiver需要发送请求、接收数据、发送ACK
|
||
- ⚠️ 需完整状态机实现
|
||
- ⚠️ Window size管理(SSH flow control)
|
||
|
||
**障碍3:Checksum计算**
|
||
- ⚠️ rsync使用rolling checksum(adler32 + MD4)
|
||
- ⚠️ 需实现rsync算法库
|
||
- ⚠️ 性能优化需求
|
||
|
||
---
|
||
|
||
### 4.2 Task 4-5实施技术障碍 ⚠️⚠️⚠️
|
||
|
||
**结论:** **存在3个HIGH级别障碍**。
|
||
|
||
**障碍矩阵:**
|
||
|
||
| 障碍级别 | 障碍描述 | 影响范围 | 解决方案 |
|
||
|---------|----------|----------|----------|
|
||
| **HIGH** ⭐⭐⭐ | **channel.read() API缺失** | Receiver模式无法实现 | 使用`into_stream()` ⭐ |
|
||
| **HIGH** ⭐⭐⭐ | **rsync protocol未标准化** | 无法实现完整握手 | 参考OpenSSH源码 |
|
||
| **HIGH** ⭐⭐⭐ | **双向交互协议复杂性** | 需完整状态机 | 需专业rsync实现 |
|
||
| MEDIUM ⭐⭐ | Channel所有权消耗 | 无法同时发送接收 | Split channel |
|
||
| MEDIUM ⭐⭐ | Window size管理 | SSH flow control | 自动管理 |
|
||
| LOW ⭐ | rsync算法实现 | Rolling checksum | 使用rsync crate |
|
||
|
||
**详细障碍分析:**
|
||
|
||
**障碍1:channel.read() API缺失 ⭐⭐⭐**
|
||
|
||
**问题:**
|
||
```rust
|
||
// 期望API(不存在)
|
||
let data = channel.read(1024).await?; // ← ❌ API不存在
|
||
|
||
// 实际替代方案
|
||
let stream = channel.into_stream(); // ← 消耗所有权 ⚠️
|
||
let n = stream.read(&mut buf).await?; // ← AsyncRead
|
||
```
|
||
|
||
**影响:**
|
||
- ⚠️ 无法在保留channel的同时读取数据
|
||
- ⚠️ 需要提前准备所有逻辑(消耗所有权)
|
||
- ⚠️ 无法动态切换Sender/Receiver模式
|
||
|
||
**解决方案:**
|
||
- ✅ 使用`into_stream()`创建双向流
|
||
- ✅ 提前解析rsync命令,确定模式
|
||
- ✅ 使用`channel.split()`分离读写(需验证)
|
||
|
||
---
|
||
|
||
**障碍2:rsync protocol未标准化 ⭐⭐⭐**
|
||
|
||
**问题:**
|
||
- ⚠️ rsync protocol无公开RFC文档
|
||
- ⚠️ 仅能参考OpenSSH源码(sftp-server.c)
|
||
- ⚠️ 版本兼容性问题(rsync 31.x vs 30.x)
|
||
|
||
**证据:**
|
||
```bash
|
||
# OpenSSH源码位置
|
||
openssh/sftp-server.c:main() ← rsync protocol实现
|
||
openssh/sftp-common.c ← 辅助函数
|
||
```
|
||
|
||
**影响:**
|
||
- ⚠️ 需逆向分析OpenSSH实现
|
||
- ⚠️ 可能遗漏协议细节
|
||
- ⚠️ 版本兼容性测试成本高
|
||
|
||
**解决方案:**
|
||
- ✅ 参考OpenSSH源码实现(开源)
|
||
- ✅ 使用rsync命令测试验证(逆向工程)
|
||
- ✅ 添加版本协商机制
|
||
|
||
---
|
||
|
||
**障碍3:双向交互协议复杂性 ⭐⭐⭐**
|
||
|
||
**问题:**
|
||
```rust
|
||
// rsync receiver需要复杂的状态机
|
||
loop {
|
||
// 1. 接收客户端请求
|
||
let request = stream.read(&mut buf).await?;
|
||
|
||
// 2. 解析请求类型
|
||
match parse_rsync_request(&request) {
|
||
RsyncRequest::FileData { checksum } => {
|
||
// 3. 计算本地checksum
|
||
let local_checksum = compute_checksum(&file)?;
|
||
|
||
// 4. 发送差异块
|
||
stream.write_all(&delta_blocks).await?;
|
||
}
|
||
RsyncRequest::Ack => {
|
||
// 5. 发送更多数据
|
||
stream.write_all(&next_chunk).await?;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**影响:**
|
||
- ⚠️ 需完整状态机实现(10+ 状态)
|
||
- ⚠️ 错误处理复杂(超时、断连)
|
||
- ⚠️ 性能优化需求(rolling checksum)
|
||
|
||
**解决方案:**
|
||
- ✅ 参考rsync crate(如果存在)
|
||
- ✅ 使用简化版rsync协议(无delta传输)
|
||
- ⚠️ 需专业rsync知识
|
||
|
||
---
|
||
|
||
### 4.3 可能的替代方案 ⭐
|
||
|
||
**方案矩阵:**
|
||
|
||
| 方案 | 技术栈 | 实现难度 | 功能完整性 | 推荐度 |
|
||
|------|--------|----------|-----------|--------|
|
||
| **方案1:简化rsync** ⭐ | russh + 自定义协议 | 中 | Sender only | ⭐⭐⭐ |
|
||
| **方案2:调用rsync命令** ⭐ | russh + rsync CLI | 低 | 完整 | ⭐⭐⭐⭐ |
|
||
| **方案3:使用SFTP** ⭐ | russh + russh-sftp | 低 | 完整 | ⭐⭐⭐⭐⭐ |
|
||
| **方案4:完整rsync实现** | russh + rsync protocol | 高 | 完整 | ⭐ |
|
||
| **方案5:使用librsync** | russh + C library | 高 | 完整 | ⭐⭐ |
|
||
|
||
---
|
||
|
||
**方案1:简化rsync实现 ⭐⭐⭐**
|
||
|
||
**实现思路:**
|
||
```rust
|
||
// 只实现Sender模式(单向发送)
|
||
pub async fn handle_rsync_sender(channel: Channel<Msg>, file_path: &str) -> Result<()> {
|
||
// Phase 1: Connection Setup
|
||
channel.data(b"rsync\n").await?;
|
||
|
||
// Phase 2: Authentication
|
||
channel.data(b"@RSYNCD: OK\n").await?;
|
||
|
||
// Phase 3: File Transfer(Sender only)
|
||
let file_data = tokio::fs::read(file_path).await?;
|
||
channel.data_bytes(Bytes::from(file_data)).await?;
|
||
|
||
// Phase 4: Exit
|
||
channel.exit_status(0).await?;
|
||
channel.close().await?;
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 实现简单(单向发送)
|
||
- ✅ 兼容rsync sender模式
|
||
- ✅ 性能高(无协议开销)
|
||
|
||
**缺点:**
|
||
- ⚠️ 不支持Receiver模式
|
||
- ⚠️ 不支持增量传输(delta)
|
||
- ⚠️ 不支持rsync高级特性(checksum、压缩)
|
||
|
||
---
|
||
|
||
**方案2:调用rsync命令 ⭐⭐⭐⭐**
|
||
|
||
**实现思路:**
|
||
```rust
|
||
// 通过SSH exec调用远程rsync命令
|
||
pub async fn handle_rsync_command(channel: Channel<Msg>, command: &str) -> Result<()> {
|
||
// 使用SSH exec运行rsync命令
|
||
channel.exec(true, command).await?;
|
||
|
||
// 读取rsync输出
|
||
loop {
|
||
match channel.wait().await {
|
||
Some(ChannelMsg::Data { data }) => {
|
||
// 处理rsync输出
|
||
process_rsync_output(data);
|
||
}
|
||
Some(ChannelMsg::ExitStatus { exit_status }) => {
|
||
log::info!("rsync exit: {}", exit_status);
|
||
break;
|
||
}
|
||
None => break,
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 完整rsync功能(调用标准命令)
|
||
- ✅ 实现简单(仅需SSH exec)
|
||
- ✅ 性能高(rsync原生实现)
|
||
|
||
**缺点:**
|
||
- ⚠️ 需要远程服务器安装rsync
|
||
- ⚠️ 无法完全控制rsync行为
|
||
- ⚠️ 安全性问题(exec命令)
|
||
|
||
---
|
||
|
||
**方案3:使用SFTP替代 ⭐⭐⭐⭐⭐**
|
||
|
||
**实现思路:**
|
||
```rust
|
||
// 使用SFTP子系统传输文件
|
||
pub async fn handle_file_transfer(channel: Channel<Msg>) -> Result<()> {
|
||
// 转换为stream
|
||
let stream = channel.into_stream();
|
||
|
||
// 运行SFTP subsystem
|
||
russh_sftp::server::run(stream, sftp_handler).await;
|
||
|
||
// SFTP提供完整文件传输功能
|
||
// - upload/download
|
||
// - directory listing
|
||
// - permission management
|
||
// - checksum verification
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ **最佳方案** ⭐⭐⭐⭐⭐
|
||
- ✅ russh-sftp已实现(2.3.0)
|
||
- ✅ 标准协议(RFC draft)
|
||
- ✅ 完整功能(upload/download/list)
|
||
- ✅ 安全认证(bcrypt)
|
||
- ✅ 性能优化(DashMap并发)
|
||
|
||
**缺点:**
|
||
- ⚠️ 不支持rsync增量传输(delta)
|
||
- ⚠️ 不兼容rsync客户端
|
||
- ⚠️ 需要客户端支持SFTP
|
||
|
||
---
|
||
|
||
**方案4:完整rsync协议实现 ⭐**
|
||
|
||
**实现思路:**
|
||
```rust
|
||
// 完整rsync protocol状态机
|
||
pub struct RsyncProtocol {
|
||
state: RsyncState,
|
||
stream: ChannelStream<Msg>,
|
||
}
|
||
|
||
impl RsyncProtocol {
|
||
pub async fn handshake(&mut self) -> Result<()> {
|
||
// Phase 1: Connection Setup
|
||
self.stream.write_all(b"rsync\n").await?;
|
||
let version = self.read_line().await?;
|
||
|
||
// Phase 2: Authentication
|
||
self.stream.write_all(b"@RSYNCD: 31\n").await?;
|
||
let command = self.read_command().await?;
|
||
|
||
// Phase 3: File Transfer(完整实现)
|
||
match self.parse_command(&command) {
|
||
Sender { path } => self.sender_mode(path).await?,
|
||
Receiver { path } => self.receiver_mode(path).await?,
|
||
}
|
||
}
|
||
|
||
async fn sender_mode(&mut self, path: &str) -> Result<()> {
|
||
// ... 复杂实现
|
||
}
|
||
|
||
async fn receiver_mode(&mut self, path: &str) -> Result<()> {
|
||
// ... 复杂实现(双向交互)
|
||
}
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 完整rsync功能
|
||
- ✅ 兼容rsync客户端
|
||
- ✅ 支持增量传输
|
||
|
||
**缺点:**
|
||
- ⚠️ **实现难度极高** ⭐⭐⭐
|
||
- ⚠️ 需完整协议知识
|
||
- ⚠️ 需专业rsync算法
|
||
- ⚠️ 开发周期长(2-4周)
|
||
|
||
---
|
||
|
||
**方案5:使用librsync(C library) ⭐⭐**
|
||
|
||
**实现思路:**
|
||
```rust
|
||
// 调用librsync C library
|
||
extern crate librsync;
|
||
|
||
pub async fn handle_rsync_with_librsync(stream: ChannelStream<Msg>) -> Result<()> {
|
||
// 使用FFI调用librsync
|
||
let delta = librsync::compute_delta(&source, &target)?;
|
||
|
||
// 发送delta数据
|
||
stream.write_all(&delta).await?;
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 使用成熟的rsync算法库
|
||
- ✅ 性能优化(C实现)
|
||
- ✅ 支持增量传输
|
||
|
||
**缺点:**
|
||
- ⚠️ 需要FFI绑定(复杂)
|
||
- ⚠️ 需要处理C/Rust内存管理
|
||
- ⚠️ librsync维护状况不明
|
||
- ⚠️ 集成成本高
|
||
|
||
---
|
||
|
||
## 5. 实施建议
|
||
|
||
### 5.1 立即可行的方案 ⭐⭐⭐⭐⭐
|
||
|
||
**推荐:方案3 - 使用SFTP替代**
|
||
|
||
**实施步骤:**
|
||
|
||
**Step 1:验证现有SFTP实现**
|
||
```bash
|
||
# 测试SFTP功能
|
||
cargo run --bin markbase-core -- sftp --user warren --port 2023
|
||
|
||
# 测试上传/下载
|
||
sftp -P 2023 warren@127.0.0.1
|
||
> put test.txt
|
||
> get test.txt
|
||
```
|
||
|
||
**Step 2:扩展SFTP功能**
|
||
```rust
|
||
// 添加checksum verification
|
||
impl Handler for SftpHandler {
|
||
async fn read(&mut self, id: u32, handle: String, offset: u64, len: u32) -> Result<Data> {
|
||
let data = self.read_file_data(handle, offset, len)?;
|
||
|
||
// 计算checksum(可选)
|
||
let checksum = md5::compute(&data);
|
||
log::info!("Checksum: {:?}", checksum);
|
||
|
||
Ok(Data { id, data })
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 3:提供rsync-like接口**
|
||
```rust
|
||
// 通过Web API提供rsync-like功能
|
||
#[derive(Serialize)]
|
||
pub struct RsyncResponse {
|
||
files_transferred: usize,
|
||
total_size: u64,
|
||
checksum: String,
|
||
duration_ms: u64,
|
||
}
|
||
|
||
// REST API
|
||
POST /api/v2/rsync/upload
|
||
POST /api/v2/rsync/download
|
||
GET /api/v2/rsync/status
|
||
```
|
||
|
||
---
|
||
|
||
### 5.2 短期可行方案 ⭐⭐⭐
|
||
|
||
**推荐:方案2 - 调用rsync命令**
|
||
|
||
**实施步骤:**
|
||
|
||
**Step 1:实现SSH exec handler**
|
||
```rust
|
||
// server.rs
|
||
async fn exec_request(&mut self, channel_id: ChannelId, command: &str) -> Result<()> {
|
||
let channel = self.get_channel(channel_id).await;
|
||
|
||
// 转换为stream
|
||
let stream = channel.into_stream();
|
||
|
||
// 启动rsync命令
|
||
let mut child = tokio::process::Command::new("rsync")
|
||
.arg("-av")
|
||
.arg("--progress")
|
||
.arg(source)
|
||
.arg(dest)
|
||
.stdout(Stdio::piped())
|
||
.spawn()?;
|
||
|
||
// 流式传输rsync输出
|
||
let mut stdout = child.stdout.take()?;
|
||
tokio::io::copy(&mut stdout, &mut stream).await?;
|
||
|
||
// 发送退出状态
|
||
let status = child.wait().await?;
|
||
stream.write_all(&format!("Exit: {}\n", status).as_bytes()).await?;
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
**Step 2:配置安全限制**
|
||
```toml
|
||
# config/sftp.toml
|
||
[exec]
|
||
allowed_commands = ["rsync", "ls", "pwd"]
|
||
max_execution_time = 300 # seconds
|
||
require_approval = true
|
||
```
|
||
|
||
---
|
||
|
||
### 5.3 长期方案 ⭐
|
||
|
||
**推荐:方案1 + 方案4混合**
|
||
|
||
**实施策略:**
|
||
|
||
**Phase 1(1-2周):实现简化Sender模式**
|
||
- ✅ 实现单向文件发送
|
||
- ✅ 基本握手流程
|
||
- ✅ Exit status处理
|
||
|
||
**Phase 2(2-3周):参考OpenSSH实现Receiver**
|
||
- ⚠️ 逆向分析OpenSSH sftp-server.c
|
||
- ⚠️ 实现双向交互协议
|
||
- ⚠️ 状态机实现
|
||
|
||
**Phase 3(1-2周):性能优化**
|
||
- ⚠️ Rolling checksum实现
|
||
- ⚠️ Delta传输算法
|
||
- ⚠️ 性能测试
|
||
|
||
---
|
||
|
||
## 6. 结论
|
||
|
||
### 6.1 russh API能力总结
|
||
|
||
| 能力项 | 状态 | 评级 |
|
||
|--------|------|------|
|
||
| **channel.data()存在性** | ✅ 存在 | ⭐⭐⭐⭐⭐ |
|
||
| **异步发送支持** | ✅ 支持 | ⭐⭐⭐⭐⭐ |
|
||
| **channel.read()存在性** | ❌ 不存在 | ⭐ |
|
||
| **双向流支持** | ⚠️ 间接支持(into_stream) | ⭐⭐⭐ |
|
||
| **Message type区分** | ✅ 支持 | ⭐⭐⭐⭐⭐ |
|
||
| **Multiplexing protocol** | ❌ 不支持 | ⭐ |
|
||
|
||
### 6.2 rsync集成可行性总结
|
||
|
||
| 集成项 | 状态 | 障碍级别 |
|
||
|--------|------|----------|
|
||
| **Sender模式** | ✅ 可行 | LOW |
|
||
| **Receiver模式** | ⚠️ 困难 | HIGH ⭐⭐⭐ |
|
||
| **完整握手流程** | ⚠️ 困难 | HIGH ⭐⭐⭐ |
|
||
| **Delta传输** | ⚠️ 极困难 | HIGH ⭐⭐⭐ |
|
||
|
||
### 6.3 最终建议
|
||
|
||
**推荐方案优先级:**
|
||
|
||
1. **方案3:SFTP替代** ⭐⭐⭐⭐⭐(立即实施)
|
||
- 原因:已实现、标准协议、完整功能
|
||
- 时间:0天(现有代码已实现)
|
||
|
||
2. **方案2:调用rsync命令** ⭐⭐⭐⭐(短期实施)
|
||
- 原因:实现简单、完整功能
|
||
- 时间:1-2天
|
||
|
||
3. **方案1:简化rsync** ⭐⭐⭐(中期实施)
|
||
- 原因:兼容性需求、可控实现
|
||
- 时间:1-2周
|
||
|
||
4. **方案4:完整rsync** ⭐(长期研究)
|
||
- 原因:技术障碍高、开发周期长
|
||
- 时间:2-4周
|
||
|
||
---
|
||
|
||
**报告完成日期:** 2026-06-09
|
||
**报告版本:** 1.0
|
||
**下次更新:** 待实施反馈后更新
|