服务:KVM和宿主机双向复制粘贴问题

结论

宿主与客机之间双向复制粘贴文件不通,不是虚拟化配置错误,也不是客户端不支持 —— 根因在客机内的 SPICE 代理(vdagent-win)剪贴板格式表只认文本与图像,没有 Windows 的「文件」剪贴板格式(CF_HDROP)。表现特征是不对称的:文本正常、文件双向静默失败(无任何弹窗)。该能力上游只在未发布的主线分支补过,正式发行版没有,项目也已停止维护。官方给的文件互传机制是走 webdav 共享盘中转(需多处改造)。本项目不采用该机制,改用客机内置的 qemu-guest-agent 走命令行搬文件,已落地为管理命令的 ls / cp / paste 三个子命令并验收通过。

现象

  • 宿主(桌面环境)与客机(Windows 10)之间,双向 Ctrl+C / Ctrl+V 文件都不能完成粘贴,无任何弹窗提示;多次尝试表现不一但都不成功。
  • 纯文本复制粘贴正常(两个方向都正常)。
  • 把文件拖拽进/出虚拟机窗口,同样不行。

排查过程(实测)

步骤动作实测结果
1核对客机内代理是否装好并在运行代理进程在用户桌面会话中运行,版本为最后一个正式发行版
2开启 SPICE 调试,抓客户端协议日志代理已连接;其上报的能力集合为 {鼠标状态、显示器配置、回复、显示配置、按需剪贴板、稀疏显示器、行尾 CRLF、剪贴板体积上限} —— 没有「剪贴板选择(selection)」能力,也没有任何文件传输能力
3看客户端是否忽略了剪贴板内容日志出现 Ignoring clipboard grab(对非标准剪贴板选择通道的丢弃);确认客户端侧剪贴板共享本身是开着的
4取出客机内代理程序二进制,检索其能力含完整的 Win32 剪贴板 API 与文件传输(file-xfer)实现,但缺文件路径解析相关调用(处理「剪贴板里的文件」必须用到它)
5对照上游源码的剪贴板格式表决定性证据,见下节
6改走 qemu-guest-agent 命令行搬文件双向均可搬,逐字节校验一致(见证据表)
7溯源官方给出的文件互传机制剪贴板传文件依赖 webdav 共享盘,见「处置与判据」

根因

客机侧 SPICE 代理(vdagent-win)最后一个正式发行版的剪贴板格式表只有两项:

Windows 剪贴板格式映射的协议格式含义
CF_UNICODETEXTVD_AGENT_CLIPBOARD_UTF8_TEXT文本
CF_DIBVD_AGENT_CLIPBOARD_IMAGE_PNG / ..._BMP图像

恰好没有 CF_HDROP —— 那是 Windows 剪贴板里表示「文件」的格式。上游主线分支在 2020-07 的一次提交中补上了这一行({CF_HDROP, {VD_AGENT_CLIPBOARD_FILE_LIST, 0}}),但从未进入任何正式发行版,官方下载目录里最新版就是客机所装的那个版本;该上游项目已停止维护。

由此产生两个方向都静默失败:

  • 宿主 → 客机:宿主复制文件后,客户端把「来的是文件」这一事实告知客机,客机代理按格式表逐个匹配,一个都不认识,直接丢弃(其内部日志串即 No supported clipboard types in client grab)。
  • 客机 → 宿主:在客机里复制文件 = 剪贴板格式 CF_HDROP,代理认不出,不产生任何通知,宿主端完全无感知。

同时,客机复制的文本命中表内第一项,所以文本双向正常 —— 这正是「文本行、文件不行」这条不对称现象的来源。

实测证据

判据实测
代理是否连上协议日志 agent connected: yes
客机上报的能力集合含「按需剪贴板」,不含剪贴板选择、不含文件传输
客机代理的剪贴板格式表仅 CF_UNICODETEXT + CF_DIB 两行
上游主线同表比发行版多一行 CF_HDROP
官方下载目录最新即客机所装版本,无更新的正式版
客户端侧行为日志 Ignoring clipboard grab
文本剪贴板双向正常
改走命令行搬文件单个文件 132638 字节,两端 sha256 完全一致

与本次改动无关的判据

排查期间恰好动过若干其它配置,逐一排除如下:

被怀疑的原因实测结论
宿主桌面是 Wayland,剪贴板内容到不了 X11 剪贴板否:宿主会话是 X11,X 侧剪贴板工具可直接读写生效
剪贴板代理没装 / 没在跑否:代理进程在用户桌面会话中运行
SPICE 通道没建立否:协议日志显示代理已连接,且文本通道正常
虚拟化软件版本旧,缺代理侧修复否:本机版本已高于任何外部报告提及的修复版本
域配置屏蔽了剪贴板 / 文件传输否:相关设备段都在
客户端不支持剪贴板或文件传输否:客户端内置完整剪贴板处理与文件传输对话框

处置与判据

① 最省事(已采纳并落地):用客机内置的 qemu-guest-agent 走命令行搬文件 —— 不改虚拟化配置、不装软件、不重启。已做成管理命令的三个子命令:

子命令作用
ls [虚拟机]列出客机桌面上的文件
cp | copy | get | pull [虚拟机] [文件名]客机桌面 → 域目录
paste | send | push [虚拟机] [文件名]域目录 → 客机桌面

约定:省略虚拟机名时取列表中第一个;省略文件名时默认全部;文件名支持通配符(需加引号,否则会被本地 shell 先按当前目录展开)。判据:双向搬文件逐字节校验一致;命令 ls 可列出客机桌面文件名与大小。

② 机制性修法(未采纳):走官方机制 —— 域里加一条 webdav 通道,客机内装 webdav 客户端与服务端组件,并把客机内代理升级到含 CF_HDROP 的版本,客户端再指定一个共享目录。这套改造能让宿主 → 客机方向的复制粘贴文件可用。未采纳的理由:改造面大、要重启域,与「配置式优雅」的偏好不符;且对反向(客机 → 宿主)同样无效。

③ 官方未实现的部分(属新需求,本档不做):客机 → 宿主方向的「复制粘贴文件」,上游源码中明确标注 not supported yet,任何配置都绕不过;该方向只能靠共享盘或上面 ① 的命令行搬文件。

边界

  • 本档只覆盖「宿主 ↔ 客机双向复制粘贴文件」这一问题;不覆盖拖拽(另一套机制),也不覆盖共享盘方案的实施细节。
  • 文本 / 图像剪贴板本来就是正常的,不在本档范围。
  • 「能力集合」「剪贴板格式表」「客户端忽略日志」均为实测或源码级证据;「上游何时补上该能力」按提交时间给出,属可复核事实。
  • 处置 ① 落地形态与判据已实测通过;② 与 ③ 为对照方案,未在本机实施。