Fix control socket paths with special characters (#1328)

This commit is contained in:
Yibo Zhuang
2026-09-07 10:40:58 -07:00
committed by GitHub
parent 3f15df9e3c
commit cdb3579c79
4 changed files with 45 additions and 4 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ struct Exec: AsyncParsableCommand {
//
// [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/
if let baseURL = vmDir.controlSocketURL.baseURL {
FileManager.default.changeCurrentDirectoryPath(baseURL.path())
FileManager.default.changeCurrentDirectoryPath(baseURL.absoluteURL.path(percentEncoded: false))
}
// Switch controlling terminal into raw mode when remote pseudo-terminal is requested
+1 -1
View File
@@ -73,7 +73,7 @@ struct IP: AsyncParsableCommand {
//
// [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/
if let baseURL = controlSocketURL.baseURL {
FileManager.default.changeCurrentDirectoryPath(baseURL.path())
FileManager.default.changeCurrentDirectoryPath(baseURL.absoluteURL.path(percentEncoded: false))
}
if let ip = try await AgentResolver.ResolveIP(controlSocketURL.relativePath) {
+2 -2
View File
@@ -22,14 +22,14 @@ class ControlSocket {
// Remove control socket file from previous "tart run" invocations,
// if any, otherwise we may get the "address already in use" error
try? FileManager.default.removeItem(atPath: controlSocketURL.path())
try? FileManager.default.removeItem(at: controlSocketURL)
// Change the current working directory to a VM's base directory
// to work around Unix domain socket 104 byte limitation [1]
//
// [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/
if let baseURL = controlSocketURL.baseURL {
FileManager.default.changeCurrentDirectoryPath(baseURL.path())
FileManager.default.changeCurrentDirectoryPath(baseURL.absoluteURL.path(percentEncoded: false))
}
do {
+41
View File
@@ -1,6 +1,10 @@
import NIO
import XCTest
@testable import tart
// Avoid NSObject.bind and Tart's Darwin type shadowing the system function.
private let bindTestSocket = bind
@available(macOS 14, *)
final class ControlSocketTests: XCTestCase {
func testInitializerCreatesControlSocketBeforeReturning() async throws {
@@ -44,6 +48,43 @@ final class ControlSocketTests: XCTestCase {
}
}
func testInitializerReplacesStaleSocketInLongEncodedPath() async throws {
let temporaryDirectory = try makeTemporaryDirectory()
let originalDirectory = FileManager.default.currentDirectoryPath
defer {
FileManager.default.changeCurrentDirectoryPath(originalDirectory)
try? FileManager.default.removeItem(at: temporaryDirectory)
}
let vmDirectory = temporaryDirectory.appendingPathComponent(
"Tart Home %# 虚拟机 " + String(repeating: "v", count: 104), isDirectory: true
)
try FileManager.default.createDirectory(at: vmDirectory, withIntermediateDirectories: false)
let socketURL = URL(fileURLWithPath: "control.sock", relativeTo: vmDirectory)
XCTAssertGreaterThan(socketURL.path.utf8.count, 104)
// Closing a POSIX socket leaves its path behind, as exiting "tart run" does.
XCTAssertTrue(FileManager.default.changeCurrentDirectoryPath(vmDirectory.path))
let expectedDirectory = FileManager.default.currentDirectoryPath
let address = try SocketAddress(unixDomainSocketPath: "control.sock")
let descriptor = socket(AF_UNIX, SOCK_STREAM, 0)
XCTAssertGreaterThanOrEqual(descriptor, 0)
XCTAssertEqual(address.withSockAddr { bindTestSocket(descriptor, $0, socklen_t($1)) }, 0)
XCTAssertEqual(close(descriptor), 0)
XCTAssertTrue(FileManager.default.changeCurrentDirectoryPath(originalDirectory))
var controlSocket: ControlSocket? = try await ControlSocket(socketURL)
let eventLoopGroup = try XCTUnwrap(controlSocket?.eventLoopGroup)
do {
let serverChannel = try XCTUnwrap(controlSocket?.serverChannel)
XCTAssertEqual(FileManager.default.currentDirectoryPath, expectedDirectory)
XCTAssertTrue(FileManager.default.fileExists(atPath: socketURL.path))
try await serverChannel.executeThenClose { _ in }
}
controlSocket = nil
try await eventLoopGroup.shutdownGracefully()
}
private func makeTemporaryDirectory() throws -> URL {
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,