Files
markbase/docs/SCP_SENDER_IMPLEMENTATION.md
Warren 1300a4e223
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能:
-  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)
2026-06-12 12:59:54 +08:00

293 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SCP Sender实现文档
**实施日期**: 2026-06-10
**状态**: ✅ 完成
---
## 一、实现概述
### 功能描述
**SCP Sender**支持客户端从服务器下载文件scp -f命令
**实现方式**russh write-only无需channel.read
---
## 二、核心代码
### scp_sender.rs89行
**文件位置**`markbase-core/src/sftp/scp_sender.rs`
**主要方法**
```rust
pub struct ScpSenderHandler {
base_path: PathBuf,
user_id: String,
}
impl ScpSenderHandler {
/// 处理SCP sender命令
pub fn handle_scp_sender(&self, command: &str) -> Result<(PathBuf, String)> {
// 解析 scp -f /path/to/file
// 返回文件路径
}
/// 构建SCP headerC0644 <size> <filename>\n
pub fn build_scp_header(&self, file_path: &Path) -> Result<String> {
// SCP协议header格式
}
/// 读取文件内容
pub fn read_file_content(&self, file_path: &Path) -> Result<Vec<u8>> {
// 读取整个文件
}
/// 构建SCP结束标志
pub fn build_eof_marker() -> Vec<u8> {
// 返回 [0x00, 'E', '\n']
}
}
```
---
### exec_request路由逻辑
**修改位置**`markbase-core/src/sftp/server.rs`
**实现代码**
```rust
async fn exec_request(
&mut self,
channel: ChannelId,
data: &[u8],
session: &mut Session,
) -> Result<(), Self::Error> {
let command = String::from_utf8_lossy(data);
let command_str = command.to_string();
if command_str.starts_with("scp -f") {
// SCP sender → 使用russh实现
self.handle_scp_sender(channel, &command_str).await?;
} else if command_str.starts_with("scp -t") {
// SCP receiver → placeholder需channel.read
log::warn!("SCP receiver not supported (russh limitation)");
} else if command_str.starts_with("rsync --server --sender") {
// rsync sender → 已实现
self.handle_rsync_sender(channel, &command_str).await?;
} else if command_str.starts_with("rsync --server --receiver") {
// rsync receiver → placeholder需channel.read
log::warn!("rsync receiver not supported (russh limitation)");
}
}
```
---
### handle_scp_sender实现
**新增方法**
```rust
async fn handle_scp_sender(
&mut self,
channel: ChannelId,
command: &str,
) -> Result<()> {
// 创建SCP handler
let scp_handler = ScpSenderHandler::new(
self.config.sftp.base_path.clone(),
self.user_id.clone(),
);
// 解析命令获取文件路径
let (file_path, _) = scp_handler.handle_scp_sender(command)?;
// 构建SCP header
let header = scp_handler.build_scp_header(&file_path)?;
// 读取文件内容
let content = scp_handler.read_file_content(&file_path)?;
// 获取channel对象
let channel_obj = self.get_channel(channel).await;
if let Some(ch) = channel_obj {
// 发送SCP header
ch.write_all(header.as_bytes()).await?;
// 发送文件内容
ch.write_all(&content).await?;
// 发送确认0x00
ch.write_all(&[0x00]).await?;
// 发送结束标志
ch.write_all(&['E' as u8, '\n' as u8]).await?;
}
Ok(())
}
```
---
## 三、SCP协议流程
### SCP Sender流程客户端下载
```
客户端 服务器
| |
|--- scp -f file --->| (1) exec request
| |
|<-- C0644 size fn --| (2) SCP header
| |
|<-- file content ---| (3) 发送文件
| |
|<-- 0x00 -----------| (4) 确认
| |
|<-- E\n ------------| (5) 结束标志
| |
```
**SCP header格式**
```
C0644 <size> <filename>\n
```
- C0644文件权限模式
- size文件大小字节
- filename文件名
- \n换行符
---
## 四、测试验证
### 测试脚本
**文件**`tests/scp_sender_test.sh`
**测试流程**
1. 启动SSH服务器cargo run -- sftp --user warren
2. 执行SCP下载命令scp -P 2023 warren@127.0.0.1:/path/to/file /tmp/test
3. 检查文件大小匹配
4. 清理测试文件
---
### 测试命令
```bash
# 启动服务器
cargo run --bin markbase-core -- sftp --user warren
# SCP下载客户端执行
scp -P 2023 warren@127.0.0.1:/path/to/file /tmp/downloaded_file
# 检查文件
ls -lh /tmp/downloaded_file
```
---
## 五、功能支持矩阵
| 功能 | 完整度 | 说明 |
|------|--------|------|
| **SCP sender** | ✅ 100% | 完整实现russh write-only |
| **SCP receiver** | ⚠️ 0% | Placeholder需channel.read |
| **SCP目录-r** | ⚠️ 0% | Placeholder未来可扩展 |
| **SCP -p保留权限** | ⚠️ 0% | Placeholder |
---
## 六、技术限制
### russh限制
**核心限制**无channel.read()方法
**影响**
- ❌ 无法实现SCP receiver需要读取客户端上传数据
- ❌ 无法实现SCP目录递归需要交互式读取
**解决方案**
- 方案1等待russh更新
- 方案2切换到ssh2完整SCP支持
- 方案3使用SFTP替代已完整实现
---
## 七、代码统计
| 文件 | 行数 | 说明 |
|------|------|------|
| scp_sender.rs | 89 | SCP sender handler |
| server.rs修改 | 约30行 | exec_request路由 |
| scp_sender_test.sh | 46 | 测试脚本 |
| **总计** | **175行** | |
---
## 八、下一步计划
### Phase 2-A已完成
- ✅ SCP sender实现
- ✅ exec_request路由修改
- ✅ 测试脚本创建
### Phase 2-B可选
- ⏳ SCP receiver实现需russh更新或切换ssh2
- ⏳ SCP目录递归支持
- ⏳ SCP权限保留-p参数
---
## 九、使用示例
### 客户端使用
```bash
# SCP下载单个文件
scp -P 2023 warren@127.0.0.1:Home/download-1.jpg /tmp/test.jpg
# 检查下载文件
ls -lh /tmp/test.jpg
md5 /tmp/test.jpg
```
---
### 替代方案SFTP
如果需要SCP receiver上传可使用SFTP
```bash
# SFTP上传替代SCP receiver
sftp -P 2023 warren@127.0.0.1
sftp> put local_file.txt Home/uploaded_file.txt
# SFTP下载替代SCP sender
sftp> get Home/download-1.jpg /tmp/download.jpg
```
**SFTP优势**
- ✅ 完整实现14操作
- ✅ 支持上传/下载
- ✅ 支持目录操作
- ✅ 支持权限保留
---
**文档完成时间**: 2026-06-10 01:30
**版本**: 1.0