- 音视频
【免费下载链接】foundation-sunshine
Sunshine fork: an enhanced sunshine, a self-hosted game streaming host for Moonlight with HDR10/HDR Vivid, virtual displays, advanced audio, optimized encoders, and a modern control panel.
本技术指南深入剖析 foundation-sunshine(Sunshine fork)在 Windows 双端(主机与 moonlight-qt 客户端)之间实现会话级目录映射的完整设计方案:它如何以「mapping_id + 相对路径」取代绝对路径、如何复用现有配对证书与 GameStream HTTPS 端口做能力发现、如何用 Boost.Beast 承载双向文件 RPC 数据面,以及第一阶段只读执行器与 smoke 验证通道的落地细节。读完本文,你将掌握该特性的配置模型、路径安全边界、连接与消息协议,以及源码中对应的实现模块与测试佐证,可直接用于理解、部署或二次开发这套文件通道。
设计定位:目录映射不是 SMB,而是会话级双向文件 RPC
按设计文档 windows_directory_mapping_design.md 的定位,本方案不把目录映射设计成 SMB 或驱动级共享,而是基于现有配对身份的双向文件 RPC 通道;虚拟盘(WinFsp/Dokany)只是 RPC 的一个前端,并非第一版核心。
核心设计原则可归纳为六条:
- 远端永远不能直接提交本机绝对路径,文件访问统一使用
mapping_id + relative_path; - 拥有本地文件系统的一端负责路径解析、权限判断和实际 I/O;
- 文件传输不进入视频、音频、输入、Limelight control stream 热路径;
- Moonlight 作为主动连接方建立双向通道,避免 NAT 与客户端防火墙问题;
- 复用现有配对证书和会话生命周期,不引入额外账号体系;
- 第一版优先可靠、安全、可调试,再考虑虚拟盘体验。
整体架构从「Windows Explorer 右键目录」到「Rust GUI / Control Panel 快速共享入口」,最终汇入 Sunshine core 与 moonlight-qt 两侧的本地目录授权、路径解析/权限检查与文件读写模块,二者通过WSS/HTTPS bidirectional file RPC相连,两端可选挂载 WinFsp/Dokany。分层边界上,Rust GUI / Control Panel 是用户体验与配置编排层(注册 Explorer 右键菜单、接收--quick-share-folder <path>、本机路径存在性预检、调本地管理 API、展示共享列表与审计摘要、发送系统通知);Sunshine core 是安全边界和数据面(持久化 mapping 配置、最终校验路径/权限/reparse point/设备授权、管理 paired client certificate 与 capability token、执行文件 I/O 与 WSS RPC、记录审计事件)。文档特别强调:Rust GUI 的校验只能提升体验,不能替代 core 校验——来自右键菜单、Web UI、配置文件或本地 API 的任何 mapping 都必须经过 core 的同一套安全规则。
双端模块划分与端口策略
Sunshine 侧模块
文档建议在 src/file_mapping/ 下组织新模块,职责分工如下:
file_mapping_core:配置、权限、路径解析、文件 I/O;file_mapping_rpc:请求/响应模型、handle 管理、流控、错误码;file_mapping_http:HTTPS/WSS 路由注册,复用 Sunshine 现有证书与客户端认证;file_mapping_config:mapping JSON 解析、默认值归一化、配置错误报告;file_mapping::service_t:内置 feature service,负责 WSS 生命周期、token store、capability 状态和 mapping store 注入。
路由注册方式参考clipboard_http::register_routes(),避免继续膨胀confighttp.cpp。为支持右键快速共享,还需新增本地管理 API(仅服务本机 Control Panel / Rust GUI,不走 GameStreamnvhttp,只负责配置管理、不负责文件数据传输):
POST /api/v1/file-mapping/mappings GET /api/v1/file-mapping/mappings PATCH /api/v1/file-mapping/mappings/{id} DELETE /api/v1/file-mapping/mappings/{id}Beast WSS 数据面默认监听file_mapping_port = 48020,避开 GameStream/RTSP 已使用的48010。该端口允许通过配置或命令行覆盖,便于多实例测试、端口冲突规避和受管环境部署;capability 响应必须返回实际监听端口。在 config.cpp 中可以看到默认值:file_mappings默认"[]",file_mapping_port默认48020,并在 config.cpp 通过int_between_f限制端口范围为[1024, 65535];nvhttp.cpp 中则将这些配置注入file_mapping_config(port 与 mappings_json)。
moonlight-qt 侧模块
建议新增app/streaming/filemappinghelperclient.{h,cpp}、app/streaming/filemappingipc.{h,cpp}与file-mapping-helper/,职责为:
Session管理 helper 生命周期;FileMappingHelperClient负责启动、停止、重启 helper,并通过 IPC 下发 host 地址、HTTPS 端口、证书、客户端私钥、本地目录配置;file-mapping-helper负责本地文件系统访问、WSS 连接、双向 RPC 处理。
该结构可直接复用现有ClipboardHelperClient、ClipboardIpc、ClipboardSync的工程模式。
能力发现
Sunshine 在serverinfo中暴露能力:
<sunshineCapabilities> <fileMapping>1</fileMapping> <fileMappingVersion>1</fileMappingVersion> </sunshineCapabilities>moonlight-qt 在NvHTTP::getServerInfo()后解析该能力,仅当目标为 Sunshine 且能力存在时启用目录映射;NVIDIA GFE 路径保持不变。
映射配置模型:远端永远看不到 local_root
核心配置模型包含两类视图。本机完整视图(拥有目录的一端保存local_root):
{ "id": "host-downloads", "name": "Downloads", "side": "host", "local_root": "D:\\Downloads", "mode": "read", "allow_delete": false, "allow_execute": false, "follow_reparse_points": false, "clients": ["client_uuid"], "max_file_size": 10737418240 }字段约束:id只能包含字母、数字、_、-;mode第一阶段只允许read,配置或管理 API 传入readwrite时 core 必须拒绝或降级为只读;allow_delete、allow_execute、follow_reparse_points第一阶段必须保持false(不开放远端删除、执行,不穿透 junction/symlink/mount point);clients列出允许访问的配对客户端 UUID;max_file_size为单文件操作上限。
远端视图则只暴露:
{ "id": "host-downloads", "name": "Downloads", "side": "host", "mode": "read", "capabilities": ["list", "read"] }远端不应看到local_root。这一设计在 file_mapping_rpc.h 中由exposed_mapping_t结构体现——它只携带id/name/side/mode/capabilities,不含任何本机路径字段。
配置注入:raw JSON array 与 base64 两种写法
第一阶段 Sunshine 通过file_mappings配置项注入 host mappings,该配置项是 JSON array:
[ { "id": "host-downloads", "name": "Downloads", "path": "C:/Users/example/Downloads", "mode": "read", "clients": ["paired-client-uuid"], "follow_reparse_points": false, "max_file_size": 0 } ]其中clients为空表示所有已配对客户端可访问,非空时只允许列出的 UUID;第一阶段运行时会跳过无效 mapping,并在日志中写 warning。
实现字段关系值得注意:配置持久化和 Web UI API 使用path表示本机真实目录;运行时file_mapping::mapping_t(见 file_mapping.h)使用local_root保存同一值;协议层和远端响应不得暴露local_root,只暴露mappingid 与相对path。
兼容性方面:手写配置仍支持 raw JSON array;Web UI / control-panel 持久化时写入base64:<json>,避免sunshine.conf行解析器把 JSON 字符串里的]、#或换行误判为配置语法。这一逻辑在 file_mapping_config.cpp 的decode_config_value中实现,仅当文本以base64:前缀开头时才解码,否则原样返回。
配置解析的强制降级逻辑
从源码看,file_mapping_config.cpp 对第一版的只读约束做了强制归一化,而不是简单地报错:
readwritemode → 写 warning「readwrite mode ignored in read-only phase」并降级为read;allow_delete: true→ 写 warning 并强制false;allow_execute: true→ 写 warning 并强制false;follow_reparse_points: true→ 写 warning 并强制false;path不是已存在目录 → warning 并跳过该 mapping。
解析结果parse_result_t同时携带mappings与warnings,即「无效项跳过 + 有效项强制安全默认值」的双保险策略。
连接流程与鉴权链条
完整连接流程为:
- Moonlight 与 Sunshine 完成配对;
- Moonlight 获取
serverinfo,发现fileMappingVersion=1; - 用户启动串流;
Session创建FileMappingHelperClient;FileMappingHelperClient启动moonlight-file-mapping-helper.exe;- 主进程通过 IPC 将 host 地址、HTTPS 端口、服务端证书、客户端证书、客户端私钥和本地映射配置传给 helper;
- helper 主动连接
wss://<sunshine-host>:<https-port>/api/v1/file-mapping/session; - Sunshine 使用已有客户端证书识别客户端 UUID;
- 双方交换
hello消息,声明协议版本和各自可共享的映射; - 文件操作均通过该双向 WSS 通道完成。
鉴权链条的三个关键点(源码均有对应实现):
- 授权依据是 HTTPS 客户端证书,不是请求里的 UUID。Moonlight 请求 capability 时可携带
IdentityManager::getUniqueId()作为诊断 hint(query 参数client_uuid或请求头X-File-Mapping-Client-UUID),Sunshine 不信任其中的 UUID,而是从客户端证书查 pairing store 得到内部 paired cert UUID。这一点在 file_mapping_http.cpp 中可以看到:make_capability_request只是把 query 或 header 中的client_uuid读出来作为请求上下文,真正的授权由register_routes传入的auth回调完成。 - 短期一次性 session token。Sunshine 只为该 UUID 签发短期一次性 token;WSS 建连后 Moonlight 第一条
hello.client_uuid必须使用 capability 返回的client_uuid,并与 token 绑定 UUID 一致。token store 实现在 file_mapping_token.h:默认 TTL 60 秒、最多 128 个 token、每客户端最多 4 个、最小签发间隔 1 秒,issue/consume支持一次性消费。 - session_url 只能来自内部可信状态。capability 响应中的
session_url不能使用请求Hostheader 拼接;正常情况下 capability 返回port和session_endpoint,Moonlight 用当前已连接的 Sunshine 主机地址自行组合 WSS 目标,避免 Host header injection。这与 file_mapping_http.cpp 中make_request_session_url只采用内部session_url(且剥离 query)的实现一致。
capability 响应结构
{ "ok": true, "enabled": true, "listening": true, "version": 1, "transport": "wss", "port": 47999, "session_endpoint": "/api/v1/file-mapping/session", "session_url": "", "session_token": "...", "client_uuid": "<sunshine-paired-cert-uuid>", "implementation": "boost.beast" }从 file_mapping_http.cpp 的make_capability_response源码看,实际响应还会额外带出control: "json"、data: "binary"、features(mappings、transfer_jobs、explicit_authorization、cancel_job)与limits(binary_header_size: 44、max_protocol_version: 1)等诊断字段,ok字段直接由state.error.empty()决定。
RPC 消息模型与错误码
控制面消息(JSON text frame)
协议版本固定为 1(file_mapping_rpc.h 中kProtocolVersion = 1)。消息类型枚举已覆盖hello/list/stat/open/read/close/mkdir/rename/remove/job_start/job_status/cancel/result/error。
Hello(声明端点角色与可共享映射):
{ "type": "hello", "version": 1, "endpoint": "client", "client_uuid": "...", "mappings": [ { "id": "client-docs", "name": "Documents", "side": "client", "mode": "read" } ] }List / Stat / Open / Read(完整请求-响应示例):
{ "type": "list", "id": 100, "mapping": "host-downloads", "path": "games/" }{ "type": "result", "id": 100, "entries": [ { "name": "setup.exe", "kind": "file", "size": 123456, "mtime": 1782345678 } ] }{ "type": "stat", "id": 101, "mapping": "host-downloads", "path": "games/setup.exe" }{ "type": "open", "id": 102, "mapping": "host-downloads", "path": "games/setup.exe", "mode": "read" }{ "type": "result", "id": 102, "handle": "h-123", "size": 123456 }{ "type": "read", "id": 103, "handle": "h-123", "offset": 0, "length": 262144 }{ "type": "data", "id": 103, "eof": false, "bytes_base64": "..." }Write / Close / Error(后续阶段语义与通用错误结构):
{ "type": "write", "id": 104, "handle": "h-456", "offset": 0, "bytes_base64": "..." }{ "type": "close", "id": 105, "handle": "h-123" }{ "type": "error", "id": 103, "code": "access_denied", "message": "Access denied" }建议错误码全集为:bad_request、unsupported_version、not_authenticated、access_denied、mapping_not_found、path_escape、not_found、already_exists、not_directory、is_directory、file_too_large、read_only、reparse_point_blocked、io_error、cancelled、rate_limited。这些错误码与 file_mapping.h 中resolve_error_e枚举(invalid_mapping_id、absolute_path、invalid_relative_path、reserved_name、path_escape、reparse_point_blocked、not_found、filesystem_error等)相互呼应,路径解析阶段的失败会映射为对应的协议错误码。
数据面:JSON base64 冒烟版与 binary frame 升级路径
当前 Sunshine 第一版只读执行器已实现:list、stat、read/read_chunk;open在当前 read-only executor 中不提供 handle 语义,必须返回unsupported_operation,不能隐式路由到read。当前read响应暂时使用 JSON + base64:
{ "type": "result", "id": 3, "ok": true, "mapping": "host-test", "path": "hello.txt", "offset": 0, "bytes_read": 11, "total_size": 11, "eof": true, "encoding": "base64", "data": "aGVsbG8gd29ybGQ=" }该形态用于第一阶段冒烟和 Moonlight 侧 UI/流程打通;大文件传输仍应按设计升级为 WebSocket binary frame,避免长期用 JSON base64 承载大块数据。实际上 file_mapping_rpc.h 已定义二进制帧魔数kBinaryMagic = 0x53464d50(ASCII 即 "SFMP")与 44 字节的binary_header_t(magic/version/flags/request_id/job_id_hash/handle_id/offset/payload_length),并提供了encode_binary_header/decode_binary_header辅助函数,为 binary frame 数据面预留了完整的帧头协议。
moonlight-qt 侧 smoke 客户端
当前 moonlight-qt 已新增app/streaming/FileMappingClient原型,用于打通第一阶段capability -> WSS -> hello -> list -> read:
fetchCapability()请求 Sunshine GameStream HTTPS 端口上的GET /api/v1/file-mapping/capability,即NvComputer::activeHttpsPort,通常是 47984,不是 Web UI 配置端口;- 请求携带 Moonlight
IdentityManager::getUniqueId()作为client_uuidquery 和X-File-Mapping-Client-UUIDheader,主要用于诊断和兼容;授权以 HTTPS 客户端证书推导出的配对 UUID 为准; - Sunshine capability 响应返回
client_uuid(pairing store 内部的配对证书 UUID); smokeRead()使用 capability 返回的session_url和独立session_token建立 WSS,实现中避免记录拼接 token 后的完整 URL;hello.client_uuid使用 capability 返回的client_uuid,随后发送list、read;- 复用 Moonlight 现有客户端证书配置和 Sunshine 服务端证书 pinning;
- 当前开发环境没有 QtWebSockets 模块,因此原型用
QSslSocket实现最小 WebSocket handshake 与 text frame 收发;只作为烟测客户端,不替代后续完整产品化传输层。
Session已接入隐藏烟测开关,串流连接成功后会在线程池中后台执行一次smokeRead(),日志输出File mapping smoke passed或失败原因;该开关默认关闭,不影响普通串流路径:
$env:MOONLIGHT_FILE_MAPPING_SMOKE="1" $env:MOONLIGHT_FILE_MAPPING_SMOKE_MAPPING="host-test" $env:MOONLIGHT_FILE_MAPPING_SMOKE_PATH="hello.txt"可选参数:MOONLIGHT_FILE_MAPPING_SMOKE_TIMEOUT_MS="5000"、MOONLIGHT_FILE_MAPPING_SMOKE_OFFSET="0"、MOONLIGHT_FILE_MAPPING_SMOKE_LENGTH="4096"。
此外还提供不依赖完整串流 UI 的独立 smoke CLI(在 moonlight-qt 仓库内构建并运行):
cd C:\Users\mohaha\source\repos\moonlight-qt mkdir build-filemapping-smoke cd build-filemapping-smoke cmd /c 'call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -arch=x64 && C:\Qt\6.8.2\msvc2022_64\bin\qmake.exe ..\filemapping-smoke\filemapping-smoke.pro -spec win32-msvc CONFIG+=debug && nmake'$env:PATH="C:\Qt\6.8.2\msvc2022_64\bin;C:\Users\mohaha\source\repos\moonlight-qt\libs\windows\lib\x64;$env:PATH" .\debug\moonlight-filemapping-smoke.exe ` --host 127.0.0.1 ` --https-port 47984 ` --server-cert C:\path\to\sunshine-server.pem ` --mapping host-test ` --path hello.txt该工具会打印client_uuid=<Moonlight uniqueid>,并使用同一个FileMappingClient执行capability -> WSS -> hello -> list -> read。注意--https-port应使用 GameStream HTTPS 端口(通常 47984);NvComputer::uuid是 Sunshine 主机 UUID,不能用于 WSS token/hello 绑定,实际 WSShello.client_uuid使用 Sunshine capability 返回的证书配对 UUID。
路径安全规则:Windows 路径处理是核心安全边界
文档将 Windows 路径处理定义为核心安全边界,规则如下:
- 网络层路径统一使用 UTF-8 和
/; - 本地文件访问统一转换为 UTF-16 Windows API;
- 请求路径必须是相对路径;
- 禁止远端传入盘符、UNC 路径、设备路径;
- 禁止
..逃逸; local_root + relative_path后必须 canonicalize,且 canonicalize 结果必须仍位于local_root下;- Windows 路径比较按大小写不敏感处理;
- 默认拒绝
FILE_ATTRIBUTE_REPARSE_POINT; - 默认拒绝系统目录作为共享根(如
C:\Windows、C:\Program Files); - 默认拒绝 Windows 保留设备名(
CON、PRN、AUX、NUL、COM1~COM9、LPT1~LPT9); - 写入必须先进入临时文件,完成后原子 rename。
这些规则在 file_mapping.cpp 中有完整落地,关键实现点:
is_valid_mapping_id(L138-L147):非空、长度 ≤ 64、仅允许字母/数字/_/-;is_safe_relative_path(L190-L229):拒绝绝对路径(前导/、\、盘符前缀has_drive_prefix、UNC 前缀is_unc_like)、拒绝./..段、拒绝段尾空格/点、拒绝 Windows 非法字符(<、>、:、"、\、|、?、*及控制字符)、拒绝保留设备名;is_reserved_windows_name(L149-L188):先去除段尾空格/点,再取小数点前基名做小写比较,覆盖con/prn/aux/nul/com1-9/lpt1-9完整列表;resolve_path(L231-L289):校验 mapping id → 校验相对路径 → 校验 root 是已存在目录 → 检查 root 是否含 reparse point →weakly_canonical归一化 → 拼接root / relative→ 检查候选路径是否含 reparse point → 再次weakly_canonical→ 用path_starts_with断言结果仍在 root 下 → 按需检查存在性 → 最后再查一次 reparse point;path_starts_with(L37-L47)在 Windows 下用ascii_lower做大小写不敏感比较,与「Windows 路径比较按大小写不敏感」规则一致;- reparse point 检测(L94-L130)在 Windows 下用
GetFileAttributesW检查FILE_ATTRIBUTE_REPARSE_POINT,并沿路径逐段扫描(contains_reparse_point),因此 junction/symlink 无论出现在 root 还是子路径都会被拦截。
性能与可靠性设计
第一版建议参数:分块大小默认 256 KiB 或 1 MiB;单连接并发请求数限制 4~8;单映射打开 handle 数限制 32;支持 request id 用于响应匹配;支持cancel消息取消长任务;支持心跳与空闲超时;大文件传输必须可恢复到明确错误状态,不允许半写文件冒充完成文件。
后续增强方向:Range 风格断点续传、文件 hash 校验、目录 watch、传输限速、压缩策略、mmap 或零拷贝优化。
WSS 传输层在 file_mapping_ws.h 中同样体现了限流思想:默认最大控制帧 1 MiB、最大二进制帧 1 MiB、最大活跃会话 32、最大写队列帧 16、每会话最大 job 数 128,且require_client_certificate = true默认强制客户端证书。
UI 方案与虚拟盘扩展
双端 UI 规划
- Sunshine Web UI:新增 "Directory Mapping" 配置页,包含启用开关、映射列表、添加本地目录、权限选择(只读/读写/允许删除)、客户端授权范围、高风险操作提示;
- moonlight-qt UI:新增客户端设置——启用目录映射、选择分享给主机的本地目录、每个主机独立授权、串流中显示远端文件入口。第一版 moonlight-qt 先做文件面板,不把盘符挂载作为默认入口。
WinFsp/Dokany 虚拟盘(可选增强)
推荐分层结构:moonlight-qt file panel -> FileMappingClient -> WSS RPC;可选Explorer mount -> WinFsp/Dokany adapter -> FileMappingClient -> WSS RPC。不推荐把 WebDAV/SMB 作为默认实现——它们会引入额外认证面、缓存语义和系统服务依赖,也不自然复用 Sunshine/Moonlight 已有配对身份。第三阶段可接入 WinFsp:WinFsp filesystem -> FileMappingClient -> WSS RPC -> Remote FileMappingProvider,挂载示例为M:\Host\Downloads、N:\Client\Documents。
关键注意事项:WinFsp 层只负责把 Windows 文件系统回调转成 RPC,权限和路径安全仍在 provider 端执行;虚拟盘失败不应影响串流;未安装 WinFsp 时仍保留文件面板能力;第一阶段只允许只读挂载(上传、删除、rename、replace 需等写入语义和冲突处理单独设计);挂载生命周期绑定 Moonlight 与对应 Sunshine 主机连接,断线、退出或主机撤销共享时必须自动卸载;挂载后本机任意程序都可能读取该盘符,UI 必须明确提示;不承诺固定盘符,默认使用可读挂载名,高级设置再允许指定盘符或挂载目录。
HTTP 与 WSS 实现分工
Sunshine 现有Simple-Web-Server适合继续承担 Web UI、REST API、GameStreamnvhttp能力发现和轻量管理接口,但不应作为完整文件映射 WSS 数据通道的主要实现,原因在于文件映射需要长期双向连接、WebSocket binary frame、ping/pong/close handshake/fragment/mask/backpressure,以及大文件分块、取消、限速和弱网恢复——Simple-Web-Server只提供较底层的on_upgrade接管点,完整 WebSocket 协议仍需自行实现。
因此推荐分工:
Simple-Web-Server -> nvhttp HTTPS /api/v1/file-mapping/capability -> Web UI /api/v1/file-mapping/config -> Web UI 管理接口 Boost.Beast / Boost.Asio -> file mapping WebSocket session -> JSON control frame -> binary data frame -> ping/pong, close, backpressure -> transfer job lifecycle从源码结构看,这一分工已落地:HTTP 路由注册由 file_mapping_http.cpp 的register_routes完成(^/api/v1/file-mapping/capability$与^/api/v1/file-mapping/session$两条 GET 路由);WSS 监听由 file_mapping_ws_server.h 的server_t承担(Boost.Asio + SSL,含start/stop/bound_port/state与active_sessions统计);会话状态机由 file_mapping_ws.h 的session_core_t实现(awaiting_hello -> ready -> closed,handle_text/handle_binary分发,handle_hello/handle_job_status/handle_cancel/handle_operation处理器,job 表由jobs_map 管理)。file_mapping_http.cpp 中make_session_placeholder_response明确返回session_not_implemented并提示「file mapping websocket sessions are served by the advertised Beast endpoint」,印证了 session 端点在 Simple-Web-Server 侧只做占位、真实数据面由 Beast 提供。
端口策略有两种:独立文件映射端口(实现简单、边界清楚,Moonlight 主动连出,适合第一版)与复用现有 HTTPS 端口(体验更统一,但需统一 acceptor 或从on_upgrade安全交接 socket,适合后续阶段)。推荐落地顺序:file_mapping_http保留 capability response/model 和后续管理接口 → 新增file_mapping_ws基于 Boost.Beast 实现 session 核心 → 第一版 capability 挂到nvhttpHTTPS 端口、WSS 使用独立动态端口 → 后续再评估统一到现有 HTTPS 端口。
分阶段实施路线与测试清单
三个阶段的目标与交付:
- Phase 1(Sunshine → Moonlight 文件访问):Sunshine 配置共享目录、moonlight-qt 文件面板浏览主机目录、支持下载;固定 read-only,不开放上传、删除、执行、reparse point 穿透。交付
file_mapping_core、Sunshine WSS session endpoint、moonlight-qtfile-mapping-helper、moonlight-qt 文件面板。 - Phase 2(双向目录映射):moonlight-qt 配置客户端共享目录,Sunshine 通过同一条 WSS 访问客户端目录,双端统一使用相同 RPC 和权限模型。交付 client-side provider、Sunshine 侧 remote client mapping registry、双向权限 UI。
- Phase 3(Windows 虚拟盘):可选安装 WinFsp,将远端目录只读挂载为盘符或目录,断线/退出/撤销共享时自动卸载。交付 Moonlight WinFsp adapter、Sunshine WinFsp adapter(可选)、挂载状态 UI、可读错误提示。
测试清单核心项:相对路径正常解析;..逃逸被拒绝;盘符路径被拒绝;UNC 路径被拒绝;junction/symlink 默认被拒绝;只读映射拒绝写入/删除/rename;大文件分块读写正确;中断传输不留下伪完成文件;helper 崩溃后不影响串流并按限制重启;reconnect 后文件通道可重新建立;未启用能力的服务器不显示目录映射入口。Windows 特定测试:中文路径、超长路径、大小写差异路径、保留设备名、文件被占用、权限不足、FAT/NTFS 不同行为。这些安全断言在仓库测试 test_file_mapping.cpp 中有对应覆盖(针对路径解析与逃逸防护的断言在多个用例中出现)。
推荐结论
优雅落地方式是:把目录映射抽象为会话级双向文件 RPC;Moonlight 主动连接 Sunshine 并复用配对证书;双端各自只暴露受控 mapping,不暴露真实绝对路径;第一版做文件面板和稳定传输;第二版完成客户端反向共享;第三版用 WinFsp 提供盘符体验。这样可以在不污染串流热路径、不引入 SMB 复杂度、不要求客户端开放端口的前提下,逐步获得接近本地盘的使用体验——这也正是本文所解析的 foundation-sunshine Windows 双端目录映射方案的核心价值所在。
- 音视频
【免费下载链接】foundation-sunshine
Sunshine fork: an enhanced sunshine, a self-hosted game streaming host for Moonlight with HDR10/HDR Vivid, virtual displays, advanced audio, optimized encoders, and a modern control panel.
相关推荐
Genkit Reflection 协议 V2 深度解析:基于 WebSocket 与 JSON-RPC 2.0 的双向反射架构
Genkit Reflection 协议 V2 深度解析:基于 WebSocket 与 JSON RPC 2.0 的双向反射架构 本文以 Genkit 仓库中的
人工智能大模型后端AI AgentRAG工具调用告别分屏烦恼!Chrome画中画扩展让你边看视频边高效工作
告别分屏烦恼!Chrome画中画扩展让你边看视频边高效工作 你是否经常遇到这样的困扰:正在观看重要的在线课程,却需要同时查阅资料或回复邮件?参加视频会议时,又不
前端Apache Pulsar TLS 双向认证配置指南:基于客户端证书的角色身份认证
Apache Pulsar TLS 双向认证配置指南:基于客户端证书的角色身份认证 导读 本文是 Apache Pulsar 安全体系中 TLS 认证(TLS
消息队列后端流处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考