Skip to content

Latest commit

 

History

History
320 lines (258 loc) · 14.9 KB

File metadata and controls

320 lines (258 loc) · 14.9 KB

Proxmox Backup Client - Windows 兼容性移植指南

项目概述

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-gnu

OpenSSL 交叉编译(静态)

cd /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_sw

编译命令

export 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


修改文件索引

1. Workspace Cargo.toml

文件: 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

2. 本地 Fork Crate(9 个)

每个 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.rsOsStrExt::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::matchestime_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/HardlinkAsRef<OsStr> Windows 用 UTF-8 lossy

更新上游时: 重新从 git checkout 复制源码,然后重新应用上述 patch。每个 fork 的 Cargo.toml 需要独立化(移除 *.workspace = true)。

3. pbs-* Crate 修改

pbs-buildcfg (pbs-buildcfg/src/lib.rs)

  • Windows 路径常量: CONFIGDIRC:\ProgramData\Proxmox\Backup
  • BACKUP_USER_NAME / BACKUP_GROUP_NAME#[cfg(unix)]

pbs-api-types

  • crypto.rs: 修复 MaybeUninit autoref lint(Rust 1.94)
  • datastore.rs: isizei64(proxmox-schema 新版 API)
  • lib.rs + tape/drive.rs: 为 enum 添加 doc comment(proxmox-schema 新版要求)

pbs-tools (pbs-tools/src/lib.rs, Cargo.toml)

  • setup_libc_malloc_opts()#[cfg(target_os = "linux")]
  • nix/libc/proxmox-sys → Unix-only 依赖

pbs-key-config (pbs-key-config/src/lib.rs, Cargo.toml)

  • 随机数生成: proxmox_sys::linux::random_dataopenssl::rand::rand_bytes
  • 文件权限设置 → #[cfg(unix)]
  • nix/proxmox-sys → Unix-only 依赖

pbs-datastore

  • 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::rand
  • catalog.rs / crypt_writer.rs / paperkey.rs: Unix 特有代码条件编译

pbs-client

  • 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: Read import → #[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_ShadowCopyCREATE_NO_WINDOW 避免终端污染)

4. proxmox-backup-client 二进制

  • Cargo.toml: 新增 proxmox-http 依赖;pbs-fuse-loop/pbs-pxar-fuse/pbs-config/proxmox-sys → Unix-only
  • main.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::Bodyproxmox_http::Body
  • bin/dump-catalog-shell-cli.rs: 整个文件 → #[cfg(unix)]

5. VSS 快照实现细节 (pbs-client/src/vss.rs)

  • 通过 PowerShell Get-WmiObject Win32_ShadowCopy 创建/查询/删除卷影副本
  • 使用 CREATE_NO_WINDOW (0x08000000) 标志启动 PowerShell,避免污染父进程终端编码
  • VssSnapshot::create(path) → 自动提取卷号 → 创建快照 → 返回设备路径
  • VssSnapshot::shadow_path(original) → 将 C:\Users\foo 映射为 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Users\foo
  • impl Drop → RAII 自动清理快照
  • VSS 需要管理员权限,失败时 warn 并继续无快照备份

6. Windows 上不可用的功能

功能 原因 处理方式
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 上游有新版本时,按以下步骤更新:

步骤 1: 合并上游代码

git fetch upstream && git merge upstream/master

解决冲突时注意保留所有 #[cfg(unix)] / #[cfg(windows)] 条件编译。

步骤 2: 更新本地 fork crate

对每个 *-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

步骤 3: 重新应用的关键 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)]

步骤 4: 验证编译

# Unix 编译不能破坏
cargo check -p proxmox-backup-client
# Windows 交叉编译
cargo check --target x86_64-pc-windows-gnu -p proxmox-backup-client

常见问题

Q: proxmox-fuse 找不到

创建空 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"

Q: 编译报 no matching package named xxx

检查 [patch] 段是否覆盖了所有 git 源中的 crate。新增的上游 crate 可能需要添加到 patch。

Q: Windows 上 VSS 报 "初始化失败"

需要以管理员身份运行。右键 PowerShell → 以管理员身份运行。

Q: 终端字体异常

已修复。VSS 模块使用 CREATE_NO_WINDOW 启动 PowerShell,不会影响父终端。


Patch 恢复指南

所有 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

恢复方法

方法 1: 一键恢复(推荐)

bash windows-patch/apply.sh

脚本会按顺序:解压 fork crate → 应用 git patch → 恢复新增文件。 失败的 patch 会报告,需参考本文档手动修复。

方法 2: 手动逐步恢复

# 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

方法 3: patch 失败时手动修复

如果上游代码变化较大导致 patch 无法应用,参考上方「修改文件索引」章节逐个手动修改。 关键原则:

  • 所有 nix/libc/proxmox-sys 的使用必须在 #[cfg(unix)]
  • 所有 std::os::unix 的使用必须在 #[cfg(unix)]
  • Windows 替代实现放在 #[cfg(windows)]#[cfg(not(unix))]

重新生成 patch

修改代码后,重新生成 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