vdi-cloud/docs/usb-any-storage-20261104.md

96 lines
6.1 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.

# 任意 U 盘透传实现(2026-11-04)
2026-11-05 补充:空的 usb-redir 端点移除已验证,但用户照片显示安全删除菜单还列出
显卡、VirtIO 网卡/串口和 SATA 控制器。仅移除 USB 端点不能保证托盘图标消失。
固定 PCI 设备属性修复准备与实际验收范围见 `fixed-pci-tray-20261105.md`。
该修复随后已在用户授权正常关机再开机后应用:四项 PCI 设备能力从 6 变为 0,
设备状态、GPU、系统盘、网络和 Sunshine 正常;真实 U 盘插拔与托盘外观待现场确认。
在已验证的真实 USB 透传链路上,将盒子端策略从固定 VID/PID/序列号扩展为
“任意纯 USB Mass Storage 设备”。键盘、鼠标、Hub 和显示设备仍由盒子本地使用。
## 行为
- 一个盒子、一个 VM 同时只导出一个存储设备;多个 U 盘同时插入时按盒子扫描顺序使用,
当前设备拔出后再接入下一个。
- 设备必须是纯存储设备,所有 USB interface class 都必须为 `08`;键鼠、Hub 和复合设备拒绝。
- 盒子检查分区是否本地挂载、是否有 holder 或作为 swap;发现本地占用时等待,不强制卸载。
- QEMU 使用 `0x08:-1:-1:-1:1` 过滤器,允许任意 USB Mass Storage,仍拒绝其他 USB 类别。
- PVE QMP watcher 在 VM/PVE 重启后重新建立运行时 `usb-redir` 设备。
- 盒子同时提供仅回环的存储就绪状态端口。PVE watcher 只在状态为 `ready` 时创建
`usb-redir`,连续 3 次确认状态为空后删除端点;因此物理 U 盘拔出后,Windows
的安全删除硬件图标也会消失。插拔瞬间使用防抖,避免短暂枚举波动造成误移除。
## 部署入口
```sh
python tools/deploy-usb-device-poc.py \
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 --any-storage
```
部署前应在 Windows 中安全弹出当前 U 盘。部署器会安装盒子监控服务、USB 隧道和
`vdi-usb-qmp-100.service`;不会修改 VM 固定配置、GPU、显示或键鼠输入链路。
旧的精确设备策略仍可用:
```sh
python tools/deploy-usb-device-poc.py \
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 \
--vendor 0951 --product 1666 --serial SERIAL
```
## 验收
至少用三只不同品牌或不同 VID/PID 的 U 盘测试:插入后 Windows 出现真实
USB Mass Storage 和盘符,读写大文件后校验 SHA-256;再测试安全弹出、拔插、
两个 U 盘同时插入,以及 VM/PVE 重启后的自动恢复。当前 Rust 单元测试 10/10、
Python QMP 测试 4/4 通过。已现场验证另一只 SanDisk 0781:5591 在 Windows
识别为 USBSTOR,F: NTFS 数据分区和 G: FAT32 UQITONG_ESP 分区均 Healthy。
多盘吞吐和 VM/PVE 重启后的恢复仍待验收;QMP watcher 已实现,但尚未现场安装。
## 插入体验优化
2026-11-04 已将当前 Windows 用户 test 的 StorageOnArrival 从 MSOpenFolder
改为 MSPromptEachTime,并保留自动播放开启。双分区装机盘仍显示两个卷,
不会再按“打开文件夹”这个默认操作自动打开两个窗口;提示数量和通知外观由 Windows 决定。
脚本 `tools/set-usb-experience.ps1` 可重复 Apply,并支持 Restore;仅修改三项
自动播放首选项,使用首次运行保存的原值回退。Windows 已执行 Apply 并回读成功。
备份路径:`C:\ProgramData\VDI\usb-experience-20261104\preferences.clixml`。
盒子监控程序使用已处理的 USB udev 事件即时唤醒,替代原来的每 2 秒轮询;
500ms 兜底扫描负责启动时已有设备和事件监听不可用的情况。活动会话直接核对
已导出的 sysfs 设备身份,不再因另一个 USB 设备扫描失败而误断开。
快速插盘时须等 `bNumInterfaces` 声明的所有接口出现后才做存储类型判断,
防止接口尚未枚举完整的存储/HID 复合设备被误接入。
日志包含 `usb_time_ms`、`local_check_ms`、`since_latest_usb_event_ms`;最后一项
仅为最近事件至导出调用的时间,不代表从物理插入至 Windows 完全可用的总时间。
盒子已离线测试并编译、部署(更新时无 U 盘连接),服务 active,udevadm 子进程正常。
旧二进制备份在 `/home/armbian/vdi-terminal/backups/usb-events-20261104-110747/`。
未重启 VM、Moonlight、Sunshine、Explorer,未修改启动分区和系统原生安全删除行为。
随后实测事件至导出调用为 5~8ms(仅盒子内部时间)。
继续排查发现发行版 usbredirect 0.13.0 的连接回调无条件订阅 G_IO_OUT,
现场导出进程曾占约 96% 的一个 CPU 核心。官方 0.15.0 的 update_watch() 按
usbredirhost_has_data_to_write() 动态监听;已从官方发布包编译私有版本:
- 源码:https://www.spice-space.org/download/usbredir/usbredir-0.15.0.tar.xz
- 源码 SHA256:`6dc2a380277688a068191245dac2ab7063a552999d8ac3ad8e841c10ff050961`
- 私有程序:`/usr/local/libexec/vdi-usbredirect`
- 二进制 SHA256:`58b58b9d9ab017d3221e44f83c0794ae2977ef9e020fcb8df959eef997ae8e4a`
- 构建脚本:`tools/build-usbredirect-private.sh`;头文件仅下载并解包,没有安装或升级系统 GLib/libusb。
- 盒子配置:`/etc/vdi-usb-monitor/device.env` 新增
`VDI_USBREDIRECT_BINARY=/usr/local/libexec/vdi-usbredirect`。
- 切换前备份:`/home/armbian/vdi-terminal/backups/usb-exporter-20261104-112031/`。
运行时 CPU 实测降至约 1%。官方 filter 29 项、serializer 1 项测试全部通过;
Rust 10 项测试通过。Windows 自动播放设置已回读验证,PowerShell 语法检查通过。
用户第一次复测仍反馈菜单慢,后续保持连接并再测后明确反馈“现在已经很快了”。
已确认不再自动打开文件夹;右下角提示和弹出菜单速度通过本次用户现场验收,
但没有测量从物理插入至菜单可用的完整耗时,也不代表长时间稳定性验收完成。
回退时先在 Windows 安全弹出并物理拔下 U 盘,再停止盒子 USB 服务,
从上述备份恢复二进制和 device.env,启动服务即可。Windows 自动播放可用
`C:\ProgramData\VDI\usb-experience-20261104\set-usb-experience.ps1 -Action Restore` 回退。