Files
markbase/docs/SCP_SENDER_IMPLEMENTATION.md
T
Warren 1300a4e223
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

6.3 KiB
Raw Blame History

SCP Sender实现文档

实施日期: 2026-06-10 状态: 完成


一、实现概述

功能描述

SCP Sender:支持客户端从服务器下载文件(scp -f命令)

实现方式russh write-only(无需channel.read


二、核心代码

scp_sender.rs89行)

文件位置markbase-core/src/sftp/scp_sender.rs

主要方法

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

实现代码

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实现

新增方法

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. 清理测试文件

测试命令

# 启动服务器
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参数)

九、使用示例

客户端使用

# 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

# 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