将 proxmox-backup-client 移植到 Windows 平台。核心策略:#[cfg(unix)] / #[cfg(windows)] 条件编译隔离平台代码,Unix 行为完全不变。
apt-get install -y gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 libclang-dev
rustup target add x86_64-pc-windows-gnucd /tmp && curl -LO https://www.openssl.org/source/openssl-3.2.1.tar.gz
tar xzf openssl-3.2.1.tar.gz && cd openssl-3.2.1
./Configure mingw64 --cross-compile-prefix=x86_64-w64-mingw32- \
--prefix=/usr/x86_64-w64-mingw32 no-shared no-async
make -j$(nproc) && make install_swexport PATH="$HOME/.cargo/bin:$PATH"
export OPENSSL_LIB_DIR=/usr/x86_64-w64-mingw32/lib64
export OPENSSL_INCLUDE_DIR=/usr/x86_64-w64-mingw32/include
export OPENSSL_STATIC=1
export REPOID="windows-build"
# 检查
cargo check --target x86_64-pc-windows-gnu -p proxmox-backup-client
# 构建
cargo build --target x86_64-pc-windows-gnu -p proxmox-backup-client --release产物: target/x86_64-pc-windows-gnu/release/proxmox-backup-client.exe
文件: Cargo.toml
所有 proxmox 上游 crate 从 Debian registry 改为 git 源或本地 fork 路径:
proxmox-time = { path = "proxmox-time-local" }
proxmox-sys = { path = "proxmox-sys-local" }
proxmox-compression = { path = "proxmox-compression-local" }
proxmox-http = { path = "proxmox-http-local", features = [...] }
proxmox-shared-memory = { path = "proxmox-shared-memory-local" }
proxmox-router = { path = "proxmox-router-local" }
proxmox-notify = { path = "proxmox-notify-local" }
pathpatterns = { path = "pathpatterns-local" }
pxar = { path = "pxar-local" }
proxmox-fuse = { path = "/tmp/proxmox-fuse-stub" } # 空 stub,仅满足 workspace 解析hyper 生态升级: hyper 0.14→1, http 0.2→1, h2 0.3→0.4, 新增 http-body-util, hyper-util
[patch] 段覆盖 git 源中的 crate:
[patch."https://git.proxmox.com/git/proxmox.git"]
proxmox-time / proxmox-sys / proxmox-compression / proxmox-http
proxmox-shared-memory / proxmox-router / proxmox-notify
[patch."https://git.proxmox.com/git/pathpatterns.git"]
pathpatterns
[patch."https://git.proxmox.com/git/pxar.git"]
pxar文件: .cargo/config.toml — 指定 Windows linker 和 git-fetch-with-cli
每个 fork 的通用模式:从 git checkout 复制 → 移除 workspace 继承 → 独立化 Cargo.toml → 条件编译 Unix 代码
| Fork 目录 | 原始来源 | 修改要点 |
|---|---|---|
proxmox-time-local/ |
proxmox.git/proxmox-time | 新增 windows_time.rs(epoch/rfc3339 纯 Rust 实现);posix.rs/tm_editor.rs → #[cfg(unix)];calendar_event.compute_next_event Windows 上 bail;daily_duration.time_match → #[cfg(unix)];bitflags 升级到 2.x |
proxmox-sys-local/ |
proxmox.git/proxmox-sys | 所有模块(fs/, linux/, mmap, process_locker, systemd 等)→ #[cfg(unix)];error::SysError trait 保持跨平台;nodename() Windows 用 COMPUTERNAME 环境变量;nix/libc → Unix-only 依赖 |
proxmox-compression-local/ |
proxmox.git/proxmox-compression | tar.rs/zip.rs 中 OsStrExt::as_bytes() 替换为跨平台辅助函数;libc::S_IF* 替换为本地常量;epoch_to_dos() 纯算法实现 |
proxmox-http-local/ |
proxmox.git/proxmox-http | connector.rs 中 TCP keepalive 设置 → #[cfg(unix)],Windows 空操作;升级到 hyper 1.x |
proxmox-shared-memory-local/ |
proxmox.git/proxmox-shared-memory | 整个 POSIX 实现(mmap/pthread_mutex)→ #[cfg(unix)];Windows 提供 stub 类型 |
proxmox-router-local/ |
proxmox.git/proxmox-router | 终端大小检测 Windows 返回 (80,24);locale 检测返回 UTF-8;nix::dir 补全改用 std::fs::read_dir |
proxmox-notify-local/ |
proxmox.git/proxmox-notify | CalendarMatcher::matches 中 time_match → #[cfg(unix)],Windows 返回 Ok(true) |
pathpatterns-local/ |
pathpatterns.git | libc::S_IF* 替换为 #[cfg(unix)] use libc::* + #[cfg(not(unix))] const S_IF*;libc → Unix-only 依赖 |
pxar-local/ |
pxar.git | OsStrExt/OsStringExt → 条件编译 + util.rs 跨平台辅助函数;FileExt impl → #[cfg(unix)];Symlink/Hardlink 的 AsRef<OsStr> Windows 用 UTF-8 lossy |
更新上游时: 重新从 git checkout 复制源码,然后重新应用上述 patch。每个 fork 的 Cargo.toml 需要独立化(移除
*.workspace = true)。
- Windows 路径常量:
CONFIGDIR→C:\ProgramData\Proxmox\Backup等 BACKUP_USER_NAME/BACKUP_GROUP_NAME→#[cfg(unix)]
crypto.rs: 修复MaybeUninitautoref lint(Rust 1.94)datastore.rs:isize→i64(proxmox-schema 新版 API)lib.rs+tape/drive.rs: 为 enum 添加 doc comment(proxmox-schema 新版要求)
setup_libc_malloc_opts()→#[cfg(target_os = "linux")]nix/libc/proxmox-sys→ Unix-only 依赖
- 随机数生成:
proxmox_sys::linux::random_data→openssl::rand::rand_bytes - 文件权限设置 →
#[cfg(unix)] nix/proxmox-sys→ Unix-only 依赖
Cargo.toml:pbs-config/libc/nix/proxmox-sys→ Unix-only 依赖lib.rs:prune/datastore/task_tracking/chunk_store/backup_info模块 →#[cfg(unix)]dynamic_index.rs/fixed_index.rs: Reader 跨平台化(Windows 用Vec+metadata()替代mmap+fstat),Writer 保持#[cfg(unix)]data_blob.rs:proxmox_lang::offsetof!→std::mem::offset_of!;随机数 →openssl::randcatalog.rs/crypt_writer.rs/paperkey.rs: Unix 特有代码条件编译
Cargo.toml:nix/libc/xdg/proxmox-sys→ Unix-only;新增filetime = "0.2"lib.rs:pxar/catalog_shell/vsock_client/pxar_backup_stream→#[cfg(unix)];Windows 用pxar_windows+pxar_backup_stream_windows+vss模块http_client.rs: 适配 hyper 1.x API(Body/Client/Response<Incoming>);RateLimiter移除;ticket 存储 →#[cfg(unix)]backup_reader.rs/backup_writer.rs: 信号处理 Windows 用tokio::signal::ctrl_c()task_log.rs:proxmox_sys::logrotate→#[cfg(unix)]tools/mod.rs: XDG 路径 Windows 用%APPDATA%;run_command→#[cfg(unix)]tools/key_source.rs:Readimport →#[cfg(unix)]- 新增
pxar_windows.rs: Windows pxar 创建/提取(std::fs::read_dir遍历,跳过 ACL/xattr/设备节点) - 新增
pxar_backup_stream_windows.rs: Windows 版 PxarBackupStream - 新增
vss.rs: VSS 快照管理(PowerShell +Get-WmiObject Win32_ShadowCopy,CREATE_NO_WINDOW避免终端污染)
Cargo.toml: 新增proxmox-http依赖;pbs-fuse-loop/pbs-pxar-fuse/pbs-config/proxmox-sys→ Unix-onlymain.rs:mount/catalog命令 →#[cfg(unix)]不注册backup中 VSS 集成: 备份前按卷创建快照,路径映射到快照路径,RAII 自动清理--include-dev→#[cfg(unix)]- 节点名: Windows 用
COMPUTERNAME环境变量 chunk_store::verify_chunk_size→ Windows 用简化范围检查delete_ticket_info→#[cfg(unix)]pxar::decoder::Decoder::from_std需要PxarVariant::Unified()包裹Box<dyn Write>→Box<dyn Write + Send>(async Send 约束)
key.rs: TTY 密码输入 →#[cfg(unix)]块包裹后续逻辑snapshot.rs:hyper::Body→proxmox_http::Bodybin/dump-catalog-shell-cli.rs: 整个文件 →#[cfg(unix)]
- 通过 PowerShell
Get-WmiObject Win32_ShadowCopy创建/查询/删除卷影副本 - 使用
CREATE_NO_WINDOW(0x08000000) 标志启动 PowerShell,避免污染父进程终端编码 VssSnapshot::create(path)→ 自动提取卷号 → 创建快照 → 返回设备路径VssSnapshot::shadow_path(original)→ 将C:\Users\foo映射为\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Users\fooimpl Drop→ RAII 自动清理快照- VSS 需要管理员权限,失败时 warn 并继续无快照备份
| 功能 | 原因 | 处理方式 |
|---|---|---|
| FUSE 挂载 (mount/map/unmap) | 依赖 libfuse | #[cfg(unix)] 不注册命令 |
| catalog shell | 依赖 nix::dir::Dir |
#[cfg(unix)] |
| vsock 客户端 | Linux 特有 | #[cfg(unix)] |
| ACL/xattr/chattr 备份 | POSIX 特有 | pxar_windows.rs 跳过 |
| 设备节点/FIFO/Socket | Unix 特有文件类型 | pxar_windows.rs 跳过 |
| TTY 密码输入 | 依赖 termios | #[cfg(unix)] bail |
| 文件系统类型检测 | 依赖 fstatfs | Windows 跳过 |
--include-dev 选项 |
依赖 st_dev | #[cfg(unix)] |
当 proxmox-backup 上游有新版本时,按以下步骤更新:
git fetch upstream && git merge upstream/master解决冲突时注意保留所有 #[cfg(unix)] / #[cfg(windows)] 条件编译。
对每个 *-local/ 目录:
# 以 proxmox-time-local 为例
PROXMOX_CHECKOUT=$(find ~/.cargo/git/checkouts/proxmox-*/ -maxdepth 1 -type d | tail -1)
# 复制上游源码(保留我们的 Cargo.toml 和新增文件)
cp $PROXMOX_CHECKOUT/proxmox-time/src/*.rs proxmox-time-local/src/
# 不要覆盖: Cargo.toml, windows_time.rs 等我们新增的文件
# 然后重新应用条件编译 patch| 文件 | 必须重新应用的修改 |
|---|---|
proxmox-time-local/src/lib.rs |
posix/tm_editor → #[cfg(unix)];添加 windows_time 模块 |
proxmox-time-local/src/calendar_event.rs |
compute_next_event 内部 #[cfg(unix)] 包裹 |
proxmox-time-local/src/daily_duration.rs |
time_match impl 块 → #[cfg(unix)] |
proxmox-time-local/src/parse_helpers.rs |
use super::daily_duration::* 无需改(已恢复跨平台) |
proxmox-sys-local/src/lib.rs |
所有模块 #[cfg(unix)];nodename() Windows 分支 |
proxmox-compression-local/src/tar.rs |
OsStrExt → 跨平台辅助函数 |
proxmox-compression-local/src/zip.rs |
同上 + libc::S_IF* → 本地常量 |
pxar-local/src/util.rs |
跨平台 os_str_to_bytes 等辅助函数 |
pathpatterns-local/src/match_list.rs |
S_IF* 常量定义 + use libc::* → #[cfg(unix)] |
# Unix 编译不能破坏
cargo check -p proxmox-backup-client
# Windows 交叉编译
cargo check --target x86_64-pc-windows-gnu -p proxmox-backup-client创建空 stub: mkdir -p /tmp/proxmox-fuse-stub/src && echo '' > /tmp/proxmox-fuse-stub/src/lib.rs,Cargo.toml 中 name = "proxmox-fuse", version = "0.1.3"。
检查 [patch] 段是否覆盖了所有 git 源中的 crate。新增的上游 crate 可能需要添加到 patch。
需要以管理员身份运行。右键 PowerShell → 以管理员身份运行。
已修复。VSS 模块使用 CREATE_NO_WINDOW 启动 PowerShell,不会影响父终端。
所有 Windows 兼容性修改已保存为 patch 文件,位于 windows-patch/ 目录。
windows-patch/
├── apply.sh # 一键恢复脚本
├── 01-workspace-cargo-toml.patch # Workspace Cargo.toml(依赖源 + patch 段)
├── 02-pbs-api-types.patch # isize→i64, doc comment, autoref 修复
├── 03-pbs-buildcfg.patch # Windows 路径常量
├── 04-pbs-tools.patch # malloc opts 条件编译
├── 05-pbs-key-config.patch # openssl::rand 替代 proxmox_sys
├── 06-pbs-datastore.patch # Reader 跨平台化, prune/datastore cfg(unix)
├── 07-pbs-client.patch # hyper 1.x 适配, 模块条件编译
├── 08-proxmox-backup-client.patch # mount/catalog 禁用, VSS 集成, Send 修复
├── 09-cargo-config.patch # .cargo/config.toml (Windows linker)
├── 10-new-pbs-client-src-pxar_windows.patch
├── 10-new-pbs-client-src-pxar_backup_stream_windows.patch
├── 10-new-pbs-client-src-vss.patch
├── proxmox-time-local.tar.gz # 9 个本地 fork crate 的完整打包
├── proxmox-sys-local.tar.gz
├── proxmox-compression-local.tar.gz
├── proxmox-http-local.tar.gz
├── proxmox-shared-memory-local.tar.gz
├── proxmox-router-local.tar.gz
├── proxmox-notify-local.tar.gz
├── pathpatterns-local.tar.gz
├── pxar-local.tar.gz
└── proxmox-fuse-stub.tar.gz
bash windows-patch/apply.sh脚本会按顺序:解压 fork crate → 应用 git patch → 恢复新增文件。 失败的 patch 会报告,需参考本文档手动修复。
# 1. 解压本地 fork crate
for f in windows-patch/*.tar.gz; do tar xzf "$f" -C .; done
tar xzf windows-patch/proxmox-fuse-stub.tar.gz -C /tmp/
# 2. 应用 patch(按编号顺序)
for p in windows-patch/0*.patch windows-patch/10-*.patch; do
git apply "$p" || git apply --3way "$p" || echo "FAILED: $p"
done
# 3. 删除旧 cargo config
rm -f .cargo/config如果上游代码变化较大导致 patch 无法应用,参考上方「修改文件索引」章节逐个手动修改。 关键原则:
- 所有
nix/libc/proxmox-sys的使用必须在#[cfg(unix)]内 - 所有
std::os::unix的使用必须在#[cfg(unix)]内 - Windows 替代实现放在
#[cfg(windows)]或#[cfg(not(unix))]内
修改代码后,重新生成 patch:
# 已跟踪文件
git diff HEAD -- Cargo.toml > windows-patch/01-workspace-cargo-toml.patch
git diff HEAD -- pbs-api-types/ > windows-patch/02-pbs-api-types.patch
git diff HEAD -- pbs-buildcfg/ > windows-patch/03-pbs-buildcfg.patch
git diff HEAD -- pbs-tools/ > windows-patch/04-pbs-tools.patch
git diff HEAD -- pbs-key-config/ > windows-patch/05-pbs-key-config.patch
git diff HEAD -- pbs-datastore/ > windows-patch/06-pbs-datastore.patch
git diff HEAD -- pbs-client/ > windows-patch/07-pbs-client.patch
git diff HEAD -- proxmox-backup-client/ > windows-patch/08-proxmox-backup-client.patch
# 本地 fork crate(重新打包)
for dir in proxmox-time-local proxmox-sys-local proxmox-compression-local \
proxmox-http-local proxmox-shared-memory-local proxmox-router-local \
proxmox-notify-local pathpatterns-local pxar-local; do
tar czf "windows-patch/${dir}.tar.gz" "$dir/"
done