Files
markbase/docs/SSH2_SFTP_IMPLEMENTATION_OPTIONS.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

7.4 KiB
Raw Blame History

ssh2 SFTP实现方案分析

分析日期: 2026-06-10 02:05 目的: 确定Phase 2 SFTP Handler实现方式


一、实现方案对比

方案A实现SFTP协议packet

原理直接实现SFTP packet协议

SFTP协议结构

SFTP Packet格式
- Length4字节packet总长度
- Type1字节操作类型SSH_FXP_INIT=1, SSH_FXP_OPEN=3等
- Request ID4字节请求ID
- Payload变长操作参数

操作类型:
- SSH_FXP_INIT (1):初始化
- SSH_FXP_VERSION (2):版本响应
- SSH_FXP_OPEN (3):打开文件
- SSH_FXP_CLOSE (4):关闭文件
- SSH_FXP_READ (5):读取文件
- SSH_FXP_WRITE (6写入文件
- SSH_FXP_LSTAT (7):获取状态
- SSH_FXP_FSTAT (8获取文件状态
- SSH_FXP_SETSTAT (9设置状态
- SSH_FXP_FSETSTAT (10设置文件状态
- SSH_FXP_OPENDIR (11打开目录
- SSH_FXP_READDIR (12读取目录
- SSH_FXP_REMOVE (13删除文件
- SSH_FXP_MKDIR (14创建目录
- SSH_FXP_RMDIR (15删除目录
- SSH_FXP_REALPATH (16真实路径
- SSH_FXP_STAT (17获取状态
- SSH_FXP_RENAME (18重命名
- SSH_FXP_READLINK (19读取链接
- SSH_FXP_SYMLINK (20创建链接

实现步骤

  1. 定义packet结构
  2. 实现packet解析read_packet
  3. 实现packet构建write_packet
  4. 实现14个操作handler
  5. 循环处理客户端请求

工作量

  • Packet解析约100行
  • Packet构建约100行
  • 14操作handler约200行
  • 总计约400行

优势

  • 完全控制协议细节
  • 可自定义功能
  • 不依赖外部crate

劣势

  • ⚠️ 工作量较大400行
  • ⚠️ 需深入理解SFTP协议
  • ⚠️ 测试复杂度高

方案B使用ssh2 crate内置SFTP (推荐)

发现ssh2 crate可能已内置SFTP支持

API探索

// ssh2 crate SFTP API假设
let sftp = session.sftp()?;  // 创建SFTP channel

// SFTP操作
sftp.open(path)?;
sftp.read(file)?;
sftp.write(file, data)?;
sftp.close(file)?;
sftp.readdir(path)?;
sftp.stat(path)?;
sftp.mkdir(path)?;
sftp.remove(path)?;
sftp.rename(old, new)?;

需要验证

  • ssh2 crate是否提供SFTP API
  • API是否完整14操作
  • 是否需要exec("sftp-server")

优势

  • 最小工作量约50行
  • 使用成熟实现
  • 降低风险

劣势

  • ⚠️ 需验证API是否存在
  • ⚠️ 可能功能受限

方案C使用系统sftp-server程序

原理调用系统sftp-server binary

实现

channel.exec(true, "/usr/lib/openssh/sftp-server")?;
// 系统sftp-server处理所有SFTP操作

优势

  • 工作量最小1行
  • 使用OpenSSH成熟实现
  • 功能完整

劣势

  • 依赖系统binarymacOS路径/usr/libexec/sftp-server
  • 无法自定义功能
  • FileTree映射不适用

二、ssh2 crate SFTP API验证

查阅ssh2 crate文档

关键问题

  • ssh2::Session是否有sftp()方法?
  • ssh2 crate是否支持SFTP subsystem

验证方法

  1. 查阅ssh2 crate文档
  2. 搜索ssh2-sftp crate
  3. 检查ssh2源码

ssh2 crate SFTP API预期

如果存在,应该类似

pub struct Session {
    pub fn sftp(&self) -> Result<Sftp>;
}

pub struct Sftp {
    pub fn open(&self, path: &Path) -> Result<File>;
    pub fn readdir(&self, path: &Path) -> Result<Vec<FileInfo>>;
    pub fn stat(&self, path: &Path) -> Result<FileInfo>;
    pub fn mkdir(&self, path: &Path) -> Result<()>;
    pub fn remove(&self, path: &Path) -> Result<()>;
    pub fn rename(&self, old: &Path, new: &Path) -> Result<()>;
}

三、方案选择建议

推荐方案方案B

理由

  1. 如果ssh2已内置SFTP → 工作量最小50行
  2. 如果不存在 → 回退方案A400行
  3. 先验证再实施(降低风险)

实施步骤

  1. 查阅ssh2 crate文档5分钟
  2. 如果API存在 → 使用方案B
  3. 如果API不存在 → 实施方案A

验证优先级

立即验证

  • cargo search ssh2-sftp
  • 查阅ssh2 crate文档
  • 检查ssh2::Session API

四、实施方案代码预览

方案B代码如果ssh2支持

// ssh2_server/sftp_handler.rs约50行
use ssh2::{Session, Sftp};
use crate::sftp::filetree::FileTreeMapper;

pub struct Sftp2Handler {
    user_id: String,
    config: Arc<SftpConfig>,
}

impl Sftp2Handler {
    pub fn handle_sftp(&self, session: &Session) -> Result<()> {
        let sftp = session.sftp()?;
        
        // 复用FileTree映射
        let mapper = FileTreeMapper::new(self.config.clone());
        
        // 处理客户端请求(简化)
        loop {
            // ⚠️ 需要研究如何读取SFTP请求packet
            // ssh2 sftp API可能需要进一步研究
        }
        
        Ok(())
    }
}

方案A代码手动实现

// ssh2_server/sftp_handler.rs约400行
use ssh2::Channel;

pub struct Sftp2Handler {
    user_id: String,
    config: Arc<SftpConfig>,
}

impl Sftp2Handler {
    pub fn handle_sftp(&self, channel: &mut Channel) -> Result<()> {
        // 1. 发送版本响应
        self.send_version(channel)?;
        
        // 2. 循环处理请求
        loop {
            let packet = self.read_packet(channel)?;
            
            match packet.type {
                SSH_FXP_OPEN => self.handle_open(channel, packet)?,
                SSH_FXP_READ => self.handle_read(channel, packet)?,
                SSH_FXP_WRITE => self.handle_write(channel, packet)?,
                SSH_FXP_CLOSE => self.handle_close(channel, packet)?,
                SSH_FXP_MKDIR => self.handle_mkdir(channel, packet)?,
                SSH_FXP_RMDIR => self.handle_rmdir(channel, packet)?,
                SSH_FXP_REMOVE => self.handle_remove(channel, packet)?,
                SSH_FXP_RENAME => self.handle_rename(channel, packet)?,
                SSH_FXP_OPENDIR => self.handle_opendir(channel, packet)?,
                SSH_FXP_READDIR => self.handle_readdir(channel, packet)?,
                SSH_FXP_REALPATH => self.handle_realpath(channel, packet)?,
                SSH_FXP_STAT => self.handle_stat(channel, packet)?,
                SSH_FXP_LSTAT => self.handle_lstat(channel, packet)?,
                _ => warn!("Unknown packet type: {}", packet.type),
            }
        }
    }
    
    fn read_packet(&self, channel: &mut Channel) -> Result<SftpPacket> {
        // 解析packet length, type, request_id, payload
        let mut buf = vec![0u8; 4];
        channel.read_exact(&mut buf)?;
        let length = u32::from_be_bytes(buf);
        
        let mut packet_buf = vec![0u8; length as usize];
        channel.read_exact(&mut packet_buf)?;
        
        // 解析packet
        ...
    }
    
    fn send_version(&self, channel: &mut Channel) -> Result<()> {
        // SSH_FXP_VERSION packet
        let version_packet = build_version_packet(3); // SFTP version 3
        channel.write_all(&version_packet)?;
        Ok(())
    }
}

五、决策建议

立即验证ssh2 SFTP API

  • 查阅ssh2 crate文档
  • 如果存在 → 方案B推荐
  • 如果不存在 → 方案A

时间评估

  • 方案B50行2小时
  • 方案A400行8小时
  • 差距6小时

方案选择完成时间: 2026-06-10 02:10 版本: 1.0