mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-15 18:59:52 +00:00
激活状态机与派生层(spec #21 D3 激活族 + D5 派生): - ActivationStateMachine:持久化活动目标(服务器或分组);激活原子成功 或点名失败(保留原目标与运行状态);目录提交后立即重展开,失效即清除 并发出停止意图(删除/禁用/变空/无效叶子各路径,无静默回退) - 组激活按显式子序递归展开有效启用叶子;组目标保持组 UUID - SslocalRuntimeDocument:完整 sslocal JSON 契约文档,凭据派生时解析, 插件字段无插件时整体省略;ManagedPluginProviding 缝按「未提供」参与 无效判定(真实受管集 #38 接通) - ActivationStateFileStore:activation.json 持久化(0700/0600、原子替换, 损坏按安全侧恢复为无活动目标);与 CatalogFileStore 共享 AtomicFileWriter
44 lines
1.7 KiB
Swift
44 lines
1.7 KiB
Swift
import Foundation
|
|
|
|
/// 敏感配置文件的原子写盘基线(spec #21 D5):目录 0700(含自愈)、临时文件
|
|
/// 创建即 0600、写全校验后原子替换;任一步失败保留原文件。catalog.json、
|
|
/// activation.json 与 #27 的运行时文件共用同一物理安全基线。
|
|
enum AtomicFileWriter {
|
|
enum WriteError: Error, Equatable {
|
|
case failure(detail: String)
|
|
}
|
|
|
|
/// 把 `data` 原子写入 `fileURL`(含父目录创建与 0700 基线自愈)。
|
|
static func write(_ data: Data, to fileURL: URL) throws {
|
|
let directory = fileURL.deletingLastPathComponent()
|
|
do {
|
|
if !FileManager.default.fileExists(atPath: directory.path) {
|
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
}
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o700], ofItemAtPath: directory.path)
|
|
} catch {
|
|
throw WriteError.failure(detail: String(describing: error))
|
|
}
|
|
let temporaryURL = directory.appendingPathComponent(
|
|
".\(fileURL.lastPathComponent).tmp-\(UUID().uuidString)")
|
|
if !FileManager.default.createFile(
|
|
atPath: temporaryURL.path,
|
|
contents: data,
|
|
attributes: [.posixPermissions: 0o600]
|
|
) {
|
|
throw WriteError.failure(detail: "无法创建临时文件 \(temporaryURL.path)")
|
|
}
|
|
do {
|
|
if FileManager.default.fileExists(atPath: fileURL.path) {
|
|
_ = try FileManager.default.replaceItemAt(fileURL, withItemAt: temporaryURL)
|
|
} else {
|
|
try FileManager.default.moveItem(at: temporaryURL, to: fileURL)
|
|
}
|
|
} catch {
|
|
try? FileManager.default.removeItem(at: temporaryURL)
|
|
throw WriteError.failure(detail: String(describing: error))
|
|
}
|
|
}
|
|
}
|